/*
 * OBB.h
 *
 * Created 6/5/2009 By Johnny Huynh
 *
 * Version 00.00.03 7/4/2009
 *
 * Copyright Information:
 * All content copyright © 2009 Johnny Huynh. All rights reserved.
 */

 #ifndef OBB_H
 #define OBB_H
 
 #ifndef IMPLEMENT_BOUNDING_SPHERE
 #define IMPLEMENT_BOUNDING_SPHERE 1
 #endif // IMPLEMENT_BOUNDING_SPHERE
 
 #ifndef IMPLEMENT_AABB
 #define IMPLEMENT_AABB 1
 #endif // IMPLEMENT_AABB
 
 // Oriented-Bounding Box
 template <typename TYPENAME> class OBB;
 template< typename TYPENAME > class GL_Object;

 typedef OBB<GLfloat> OBBf;
 typedef OBB<GLdouble> OBBd;
 
 #include <limits>
 #include "Vector3.h"
 #include "OrientationMatrix.h"
 
 #include "GL_Render_Shape.h"

 template <typename TYPENAME>
 class OBB
 {
 // Data Members
 protected:
 #if IMPLEMENT_BOUNDING_SPHERE == 1
    TYPENAME radius;
 #endif // IMPLEMENT_BOUNDING_SPHERE == 1
 #if GL_OBJECT_COLLISION_DETECTION_TYPE == 1
    bool isMaintained;
 #endif // GL_OBJECT_COLLISION_DETECTION_TYPE == 1
	TYPENAME half_width, half_height, half_depth; // half dimensions
	const Vector3<TYPENAME> raw_center; // (x, y, z) - center point of this OBB relative to object at position (0, 0, 0)
	                                    // and orientation( 1.0f, 0.0f, 0.0f,
	                                    //                  0.0f, 1.0f, 0.0f,
	                                    //                  0.0f, 0.0f, 1.0f )
	                                    // raw_center must already consider the raw_orientation
	const OrientationMatrix3<TYPENAME> raw_orientation; // orientation of this OBB relative to the object at 
	                                                    // orientation ( 1.0f, 0.0f, 0.0f,
	                                                    //               0.0f, 1.0f, 0.0f,
	                                                    //               0.0f, 0.0f, 1.0f )
	Vector3<TYPENAME> center; // (x, y, z) - center point of this OBB
	OrientationMatrix3<TYPENAME> orientation; // orientation of this OBB
	const GL_Object<TYPENAME>* object; // pointer to the position and orientation of the object

 // Local Functions
 public:
    /*OBB( const TYPENAME& half_width = 1.0f, const TYPENAME& half_height = 1.0f, const TYPENAME& half_depth = 1.0f,
         const TYPENAME& centerx = 0.0f,  const TYPENAME& centery = 0.0f,   const TYPENAME& centerz = 0.0f,
         const OrientationMatrix3<TYPENAME>& orientation = OrientationMatrix3<TYPENAME>( 1.0f, 0.0f, 0.0f,
                                                                                         0.0f, 1.0f, 0.0f,
                                                                                         0.0f, 0.0f, 1.0f ) );*/
    OBB( const GL_Object<TYPENAME>* const object,
 #if IMPLEMENT_BOUNDING_SPHERE == 1
         const TYPENAME& radius = 0.0f,
 #endif // IMPLEMENT_BOUNDING_SPHERE == 1
         const TYPENAME& half_width = 0.0f, const TYPENAME& half_height = 0.0f, const TYPENAME& half_depth = 0.0f,
         const TYPENAME& raw_centerx = -1.17549E-38f, 
         const TYPENAME& raw_centery = -1.17549E-38f, 
         const TYPENAME& raw_centerz = -1.17549E-38f,
         const OrientationMatrix3<TYPENAME>& raw_orientation = OrientationMatrix3<TYPENAME>( 1.0f, 0.0f, 0.0f,
                                                                                             0.0f, 1.0f, 0.0f,
                                                                                             0.0f, 0.0f, 1.0f ) );
    //OBB( const OBB& obb );
    OBB( const OBB& obb, const GL_Object<TYPENAME>* const object );
    ~OBB();
    inline OBB<TYPENAME>& operator=( const OBB<TYPENAME>& obb );
    inline const OrientationMatrix3<TYPENAME>& getRawOrientation() const;
    inline const OrientationMatrix3<TYPENAME>& getOrientation() const;
    inline const TYPENAME& getHalfWidth() const;
    inline const TYPENAME& getHalfHeight() const;
    inline const TYPENAME& getHalfDepth() const;
    
 // Friend Functions
 #if GL_OBJECT_COLLISION_DETECTION_TYPE == 0
    template <typename TYPENAME> friend inline bool collide( const OBB<TYPENAME>& obbA, const OBB<TYPENAME>& obbB );
 #endif // #if GL_OBJECT_COLLISION_DETECTION_TYPE == 0
 #if GL_OBJECT_COLLISION_DETECTION_TYPE == 1
    template <typename TYPENAME> friend inline bool collide( OBB<TYPENAME>& obbA, OBB<TYPENAME>& obbB );
    template <typename TYPENAME> friend inline void markForMaintenance( OBBTree<TYPENAME>& obbtree );
 #endif // #if GL_OBJECT_COLLISION_DETECTION_TYPE == 1
 #if IMPLEMENT_INTERPOLATION == 1
    template< typename TYPENAME > friend inline TYPENAME collisionExtent( OBB<TYPENAME>& obbA, OBB<TYPENAME>& obbB, 
                                                Vector3<TYPENAME>& obbA_displacement, Vector3<TYPENAME>& obbB_displacement );
    template <typename TYPENAME> friend inline TYPENAME collisionExtent( const OBB<TYPENAME>& obbA, const OBB<TYPENAME>& obbB,
        const Vector3<TYPENAME>& obbA_displacement, const Vector3<TYPENAME>& obbB_displacement, const Vector3<TYPENAME>& axis,        const TYPENAME half_extent_of_both_OBB, const Vector3<TYPENAME>& T, const Vector3<TYPENAME>& netUnitVelocity );
    /*template <typename TYPENAME> friend inline TYPENAME collisionExtent( const OBB<TYPENAME>& obbA, const OBB<TYPENAME>& obbB,                                       const Vector3<TYPENAME>& obbA_displacement, const Vector3<TYPENAME>& obbB_displacement,
                                       const Vector3<TYPENAME>& axis );*/
 #endif // IMPLEMENT_INTERPOLATION == 1 
    template <typename TYPENAME> friend inline OBB<TYPENAME>& maintain( OBB<TYPENAME>& obb );
    template <typename TYPENAME> friend inline void print( const OBB<TYPENAME>& obb );
    template <typename TYPENAME> friend inline void render( const OBB<TYPENAME>& obb );
 #if IMPLEMENT_BOUNDING_SPHERE == 1
    template <typename TYPENAME> friend inline void renderBoundingSphere( const OBB<TYPENAME>& obb );
 #endif // IMPLEMENT_BOUNDING_SPHERE == 1
 #if IMPLEMENT_AABB == 1
    template <typename TYPENAME> friend inline void renderAABB( const OBB<TYPENAME>& obb );
 #endif // IMPLEMENT_AABB == 1
 };

 /** LOCAL FUNCTIONS **/
 
 /**
  * Alternative Constructor
  */
 /*template< typename TYPENAME >
 OBB<TYPENAME>::OBB( const TYPENAME& half_width, const TYPENAME& half_height, const TYPENAME& half_depth,
                     const TYPENAME& centerx,  const TYPENAME& centery,   const TYPENAME& centerz,
                     const OrientationMatrix3<TYPENAME>& orientation )
                            : object( *new GL_Actor() ), deleteObjectOnDestruct( true ),
                              half_width( half_width ), half_height( half_height ), half_depth( half_depth ), 
                              center( centerx, centery, centerz ), orientation( orientation )
 {
    maintain( *this );
 }*/
 
 /**
  * Constructor
  */
 template< typename TYPENAME >
 OBB<TYPENAME>::OBB( const GL_Object<TYPENAME>* const object, 
 #if IMPLEMENT_BOUNDING_SPHERE == 1
                     const TYPENAME& radius,
 #endif // IMPLEMENT_BOUNDING_SPHERE == 1
                     const TYPENAME& half_width, const TYPENAME& half_height, const TYPENAME& half_depth,
                     const TYPENAME& raw_centerx, const TYPENAME& raw_centery, const TYPENAME& raw_centerz,
                     const OrientationMatrix3<TYPENAME>& raw_orientation )
                            : object( object ),
 #if IMPLEMENT_BOUNDING_SPHERE == 1
                              radius( radius ),
 #endif // IMPLEMENT_BOUNDING_SPHERE == 1
 #if GL_OBJECT_COLLISION_DETECTION_TYPE == 1
                              isMaintained( true ),
 #endif // GL_OBJECT_COLLISION_DETECTION_TYPE == 1
                              half_width( half_width ), half_height( half_height ), half_depth( half_depth ), 
                              raw_center( raw_centerx, raw_centery, raw_centerz ), raw_orientation( raw_orientation )         
 {
    maintain( *this );
 }
 
 /**
  * Alternative Constructor
  */
 /*template <typename TYPENAME>
 OBB<TYPENAME>::OBB( const OBB& obb ) 
               : object( obb.object )
 {
    memcpy( this, &obb, sizeof( OBB<TYPENAME> ) );
 }*/
 
 /**
  * Alternative Constructor
  */
 template <typename TYPENAME>
 OBB<TYPENAME>::OBB( const OBB<TYPENAME>& obb, const GL_Object<TYPENAME>* const object ) 
               : object( object ),
 #if IMPLEMENT_BOUNDING_SPHERE == 1
                 radius( obb.radius ),
 #endif // IMPLEMENT_BOUNDING_SPHERE == 1
 #if GL_OBJECT_COLLISION_DETECTION_TYPE == 1
                 isMaintained( obb.isMaintained ),
 #endif // GL_OBJECT_COLLISION_DETECTION_TYPE == 1
                 half_width( obb.half_width ), half_height( obb.half_height ), half_depth( obb.half_depth ), 
                 raw_center( obb.raw_center ), raw_orientation( obb.raw_orientation ),
                 center( obb.center ), orientation( obb.orientation )
 {
    //const GL_Actor* temp = object;
    //memcpy( this, &obb, sizeof( OBB<TYPENAME> ) );
    //this->object = object;
 }
 
 /**
  * Destructor
  */
 template< typename TYPENAME >
 OBB<TYPENAME>::~OBB()
 {

 }
 
 /**
  * operator=() copies the content of the specified OBB to this OBB.
  *
  * @param (const OBB<TYPENAME>&) obb
  */
 template< typename TYPENAME >
 inline OBB<TYPENAME>& OBB<TYPENAME>::operator=( const OBB<TYPENAME>& obb )
 {
    const GL_Object<TYPENAME>* temp = object;
    memcpy( this, &obb, sizeof( OBB<TYPENAME> ) );
    object = temp;
    /*print( *this );
    print( obb );
    printf("Obb's object: %1d\n", object );
    printf("Copied obb's object: %1d\n", obb.object );*/
    
    return *this;
 }
 
 /**
  * getRawOrientation() returns the raw_orientation of this OBB.
  *
  * @return const OrientationMatrix3<TYPENAME>&
  */
 template < typename TYPENAME >
 inline const OrientationMatrix3<TYPENAME>& OBB<TYPENAME>::getRawOrientation() const
 {
    return raw_orientation;
 }
 
 /**
  * getOrientation() returns the orientation of this OBB.
  *
  * @return const OrientationMatrix3<TYPENAME>&
  */
 template < typename TYPENAME >
 inline const OrientationMatrix3<TYPENAME>& OBB<TYPENAME>::getOrientation() const
 {
    return orientation; //object.getOrientation() * orientation;
 }
 
 /**
  * getHalfWidth() returns half the width of this OBB.
  *
  * @return const TYPENAME&
  */
 template < typename TYPENAME >
 inline const TYPENAME& OBB<TYPENAME>::getHalfWidth() const
 {
    return half_width;
 }
 
 /**
  * getHalfHeight() returns half the height of this OBB.
  *
  * @return const TYPENAME&
  */
 template < typename TYPENAME >
 inline const TYPENAME& OBB<TYPENAME>::getHalfHeight() const
 {
    return half_height;
 }
 
 /**
  * getHalfDepth() returns the half the depth of this OBB.
  *
  * @return const TYPENAME&
  */
 template < typename TYPENAME >
 inline const TYPENAME& OBB<TYPENAME>::getHalfDepth() const
 {
    return half_depth;
 }
 
 /** FRIEND FUNCTIONS **/
 
 #if GL_OBJECT_COLLISION_DETECTION_TYPE == 0
 /**
  * collide() returns true if the two specified OBBs are in collision; otherwise,
  * false is returned.
  *
  * @param (OBB<TYPENAME>&) obbA
  * @param (OBB<TYPENAME>&) obbB
  * @return bool
  */
 template< typename TYPENAME >
 inline bool collide( const OBB<TYPENAME>& obbA, const OBB<TYPENAME>& obbB )
 {
    //Vector3<TYPENAME> obbA_center = (obbA.object->getOrientation() * obbA.raw_center) + obbA.object->getPosition();
    //Vector3<TYPENAME> obbB_center = (obbB.object->getOrientation() * obbB.raw_center) + obbB.object->getPosition();
 
 #if GL_OBJECT_COLLISION_DETECTION_TYPE == 1
    if ( !obbA.isMaintained )
        maintain( obbA );
    if ( !obbB.isMaintained )
        maintain( obbB );
 #endif // GL_OBJECT_COLLISION_DETECTION_TYPE == 1
 
    // Translation vector that moves the position of B relative to the position of A
    // (0, 0, 0) = PA - PA
    // T = PB – PA
    Vector3<TYPENAME> T( obbB.center - obbA.center );
 #if IMPLEMENT_BOUNDING_SPHERE == 1
     if (global::enableBoundingSphere)
     {
        if ( magnitude_squared( T ) > (obbA.radius + obbB.radius) * (obbA.radius + obbB.radius) )
            return false;
     }
 #endif // IMPLEMENT_BOUNDING_SPHERE == 1
 #if IMPLEMENT_AABB == 1
     if (global::enableAABB)
     {
        if ( fabs(T.x) > (obbA.half_width * fabs(obbA.orientation.Xx)) + (obbA.half_height * fabs(obbA.orientation.Yx)) 
                       + (obbA.half_depth * fabs(obbA.orientation.Zx)) 
                       + (obbB.half_width * fabs(obbB.orientation.Xx)) + (obbB.half_height * fabs(obbB.orientation.Yx)) 
                       + (obbB.half_depth * fabs(obbB.orientation.Zx)) )
                        return false;
                        
        if ( fabs(T.y) > (obbA.half_width * fabs(obbA.orientation.Xy)) + (obbA.half_height * fabs(obbA.orientation.Yy)) 
                       + (obbA.half_depth * fabs(obbA.orientation.Zy)) 
                       + (obbB.half_width * fabs(obbB.orientation.Xy)) + (obbB.half_height * fabs(obbB.orientation.Yy)) 
                       + (obbB.half_depth * fabs(obbB.orientation.Zy)) )
                        return false;
                        
        if ( fabs(T.z) > (obbA.half_width * fabs(obbA.orientation.Xz)) + (obbA.half_height * fabs(obbA.orientation.Yz)) 
                       + (obbA.half_depth * fabs(obbA.orientation.Zz))
                       + (obbB.half_width * fabs(obbB.orientation.Xz)) + (obbB.half_height * fabs(obbB.orientation.Yz)) 
                       + (obbB.half_depth * fabs(obbB.orientation.Zz)) )
                        return false;
     }
 #endif // IMPLEMENT_AABB == 1
    
    // T * R * I
    //const OrientationMatrix3<TYPENAME>& obbA_orientation( obbA.orientation ); //obbA.object->getOrientation() * obbA.raw_orientation );
    //const OrientationMatrix3<TYPENAME>& obbB_orientation( obbB.orientation ); //obbB.object->getOrientation() * obbB.raw_orientation );
    
    // The axes for obbA
    Vector3<TYPENAME> Ax( obbA.orientation.getXAxis() );
    Vector3<TYPENAME> Ay( obbA.orientation.getYAxis() );
    Vector3<TYPENAME> Az( obbA.orientation.getZAxis() );
    
    // The axes for obbB
    Vector3<TYPENAME> Bx( obbB.orientation.getXAxis() );
    Vector3<TYPENAME> By( obbB.orientation.getYAxis() );
    Vector3<TYPENAME> Bz( obbB.orientation.getZAxis() );
    
    // Rotation Matrix that rotates B relative to A's axes
    // I = transpose(A) * A
    // R = transpose(A) * B
    // Rij = Ai • Bj 
    OrientationMatrix3<TYPENAME> R( Ax*Bx, Ay*Bx, Az*Bx,
                                    Ax*By, Ay*By, Az*By,
                                    Ax*Bz, Ay*Bz, Az*Bz );
    
    const TYPENAME& WA( obbA.half_width );
    const TYPENAME& HA( obbA.half_height );
    const TYPENAME& DA( obbA.half_depth );
    const TYPENAME& WB( obbB.half_width );
    const TYPENAME& HB( obbB.half_height );
    const TYPENAME& DB( obbB.half_depth );
    
    // The 6 separating planes for the faces of the two boxes
    
    // Case 1:
    // L = Ax
    // | T*Ax | > WA + | WB * Rxx | + | HB * Rxy | + | DB * Rxz |
    if ( fabs( T*Ax ) > WA + fabs( WB*R.Xx ) + fabs( HB*R.Xy ) + fabs( DB*R.Xz ) )
        return false;
    
    // Case 2:
    // L = Ay
    // | T*Ay | > HA + | WB * Ryx | + | HB * Ryy | + | DB * Ryz |
    if ( fabs( T*Ay ) > HA + fabs( WB*R.Yx ) + fabs( HB*R.Yy ) + fabs( DB*R.Yz ) )
        return false;
    
    // Case 3:
    // L = Az
    // | T*Az | > DA + | WB * Rzx | + | HB * Rzy | + | DB * Rzz |
    if ( fabs( T*Az ) > DA + fabs( WB*R.Zx ) + fabs( HB*R.Zy ) + fabs( DB*R.Zz ) )
        return false;
    
    // Case 4:
    // L = Bx
    // | T*Bx | > WB + | WA * Rxx | + | HA * Ryx | + | DA * Rzx |
    if ( fabs( T*Bx ) > WB + fabs( WA*R.Xx ) + fabs( HA*R.Yx ) + fabs( DA*R.Zx ) )
        return false;
    
    // Case 5:
    // L = By
    // | T*By | > HB + | WA * Rxy | + | HA * Ryy | + | DA * Rzy |
    if ( fabs( T*By ) > HB + fabs( WA*R.Xy ) + fabs( HA*R.Yy ) + fabs( DA*R.Zy ) )
        return false;
    
    // Case 6:
    // L = Bz
    // | T*Bz | > DB + | WA * Rxz | + | HA * Ryz | + | DA * Rzz |
    if ( fabs( T*Bz ) > DB + fabs( WA*R.Xz ) + fabs( HA*R.Yz ) + fabs( DA*R.Zz ) )
        return false;
    
    // The 9 separating planes for the edges of the two boxes
    
    // Case 7:
    // L = Ax x Bx
    // |(T*Az)*Ryx - (T*Ay)*Rzx| > |HA*Rzx| + |DA*Ryx| + |HB*Rxz| + |DB*Rxy|
    if ( fabs( (T*Az*R.Yx) - (T*Ay*R.Zx) ) > 
                            fabs( HA*R.Zx ) + fabs( DA*R.Yx ) + fabs( HB*R.Xz ) + fabs( DB*R.Xy ) )
        return false;
    
    // Case 8:
    // L = Ax x By
    // |(T*Az)*Ryy - (T*Ay)*Rzy| > |HA*Rzy| + |DA*Ryy| + |WB*Rxz| + |DB*Rxx|
    if ( fabs( (T*Az*R.Yy) - (T*Ay*R.Zy) ) > 
                            fabs( HA*R.Zy ) + fabs( DA*R.Yy ) + fabs( WB*R.Xz ) + fabs( DB*R.Xx ) )
        return false;
    
    // Case 9:
    // L = Ax x Bz
    // |(T*Az)*Ryz - (T*Ay)*Rzz| > |HA*Rzz| + |DA*Ryz| + |WB*Rxy| + |HB*Rxx|
    if ( fabs( (T*Az*R.Yz) - (T*Ay*R.Zz) ) > 
                            fabs( HA*R.Zz ) + fabs( DA*R.Yz ) + fabs( WB*R.Xy ) + fabs( HB*R.Xx ) )
        return false;
    
    // Case 10:
    // L = Ay x Bx
    // |(T*Ax)*Rzx - (T*Az)*Rxx| > |WA*Rzx| + |DA*Rxx| + |HB*Ryz| + |DB*Ryy|
    if ( fabs( (T*Ax*R.Zx) - (T*Az*R.Xx) ) > 
                            fabs( WA*R.Zx ) + fabs( DA*R.Xx ) + fabs( HB*R.Yz ) + fabs( DB*R.Yy ) )
        return false;
    
    // Case 11:
    // L = Ay x By
    // |(T*Ax)*Rzy - (T*Az)*Rxy| > |WA*Rzy| + |DA*Rxy| + |WB*Ryz| + |DB*Ryx|
    if ( fabs( (T*Ax*R.Zy) - (T*Az*R.Xy) ) > 
                            fabs( WA*R.Zy ) + fabs( DA*R.Xy ) + fabs( WB*R.Yz ) + fabs( DB*R.Yx ) )
        return false;
    
    // Case 12:
    // L = Ay x Bz
    // |(T*Ax)*Rzz - (T*Az)*Rxz| > |WA*Rzz| + |DA*Rxz| + |WB*Ryy| + |HB*Ryx|
    if ( fabs( (T*Ax*R.Zz) - (T*Az*R.Xz) ) > 
                            fabs( WA*R.Zz ) + fabs( DA*R.Xz ) + fabs( WB*R.Yy ) + fabs( HB*R.Yx ) )
        return false;
    
    // Case 13:
    // L = Az x Bx
    // |(T*Ay)*Rxx - (T*Ax)*Ryx| > |WA*Ryx| + |HA*Rxx| + |HB*Rzz| + |DB*Rzy|
    if ( fabs( (T*Ay*R.Xx) - (T*Ax*R.Yx) ) > 
                            fabs( WA*R.Yx ) + fabs( HA*R.Xx ) + fabs( HB*R.Zz ) + fabs( DB*R.Zy ) )
        return false;
    
    // Case 14:
    // L = Az x By
    // |(T*Ay)*Rxy - (T*Ax)*Ryy| > |WA*Ryy| + |HA*Rxy| + |WB*Rzz| + |DB*Rzx|
    if ( fabs( (T*Ay*R.Xy) - (T*Ax*R.Yy) ) > 
                            fabs( WA*R.Yy ) + fabs( HA*R.Xy ) + fabs( WB*R.Zz ) + fabs( DB*R.Zx ) )
        return false;
    
    // Case 15:
    // L = Az x Bz
    // |(T*Ay)*Rxz - (T*Ax)*Ryz| > |WA*Ryz| + |HA*Rxz| + |WB*Rzy| + |HB*Rzx|
    if ( fabs( (T*Ay*R.Xz) - (T*Ax*R.Yz) ) > 
                            fabs( WA*R.Yz ) + fabs( HA*R.Xz ) + fabs( WB*R.Zy ) + fabs( HB*R.Zx ) )
        return false;
    
    return true;
 }
 #endif // GL_OBJECT_COLLISION_DETECTION_TYPE == 0
 
 #if GL_OBJECT_COLLISION_DETECTION_TYPE == 1
 /**
  * collide() returns true if the two specified OBBs are in collision; otherwise,
  * false is returned.
  *
  * @param (OBB<TYPENAME>&) obbA
  * @param (OBB<TYPENAME>&) obbB
  * @return bool
  */
 template< typename TYPENAME >
 inline bool collide( OBB<TYPENAME>& obbA, OBB<TYPENAME>& obbB )
 {
    //Vector3<TYPENAME> obbA_center = (obbA.object->getOrientation() * obbA.raw_center) + obbA.object->getPosition();
    //Vector3<TYPENAME> obbB_center = (obbB.object->getOrientation() * obbB.raw_center) + obbB.object->getPosition();
 
 #if IMPLEMENT_BOUNDING_SPHERE == 1
 
    // Additional performance may be gained by separating isMaintained into isCenterMaintained and 
    // isOrientationMaintained, but the gain is not certain because an object that requires maintainence
    // in the first place, will likely be a mobile object that moves often.
    if ( !obbA.isMaintained )
        obbA.center = (obbA.object->getOrientation() * obbA.raw_center) + obbA.object->getPosition();
    if ( !obbB.isMaintained )
        obbB.center = (obbB.object->getOrientation() * obbB.raw_center) + obbB.object->getPosition();
    
 #endif // IMPLEMENT_BOUNDING_SPHERE == 1
    
    // Translation vector that moves the position of B relative to the position of A
    // (0, 0, 0) = PA - PA
    // T = PB – PA
    Vector3<TYPENAME> T( obbB.center - obbA.center );
 
 #if IMPLEMENT_BOUNDING_SPHERE == 1
    
    if (global::enableBoundingSphere)
    {
        if ( magnitude_squared( T ) > (obbA.radius + obbB.radius) * (obbA.radius + obbB.radius) )
            return false;
    }
    
    if ( !obbA.isMaintained )
    {
        obbA.orientation = obbA.object->getOrientation() * obbA.raw_orientation;
        obbA.isMaintained = true;
    }
    if ( !obbB.isMaintained )
    {
        obbB.orientation = obbB.object->getOrientation() * obbB.raw_orientation;
        obbB.isMaintained = true;
    }
        
 #endif // IMPLEMENT_BOUNDING_SPHERE == 1
 
 #if IMPLEMENT_BOUNDING_SPHERE == 0
    if ( !obbA.isMaintained )
        maintain( obbA );
    if ( !obbB.isMaintained )
        maintain( obbB );
 #endif // IMPLEMENT_BOUNDING_SPHERE == 0
        
 #if IMPLEMENT_AABB == 1
    if (global::enableAABB)
    {
        if ( fabs(T.x) > (obbA.half_width * fabs(obbA.orientation.Xx)) + (obbA.half_height * fabs(obbA.orientation.Yx)) 
                       + (obbA.half_depth * fabs(obbA.orientation.Zx)) 
                       + (obbB.half_width * fabs(obbB.orientation.Xx)) + (obbB.half_height * fabs(obbB.orientation.Yx)) 
                       + (obbB.half_depth * fabs(obbB.orientation.Zx)) )
                        return false;
                        
        if ( fabs(T.y) > (obbA.half_width * fabs(obbA.orientation.Xy)) + (obbA.half_height * fabs(obbA.orientation.Yy)) 
                       + (obbA.half_depth * fabs(obbA.orientation.Zy)) 
                       + (obbB.half_width * fabs(obbB.orientation.Xy)) + (obbB.half_height * fabs(obbB.orientation.Yy)) 
                       + (obbB.half_depth * fabs(obbB.orientation.Zy)) )
                        return false;
                        
        if ( fabs(T.z) > (obbA.half_width * fabs(obbA.orientation.Xz)) + (obbA.half_height * fabs(obbA.orientation.Yz)) 
                       + (obbA.half_depth * fabs(obbA.orientation.Zz))
                       + (obbB.half_width * fabs(obbB.orientation.Xz)) + (obbB.half_height * fabs(obbB.orientation.Yz)) 
                       + (obbB.half_depth * fabs(obbB.orientation.Zz)) )
                        return false;
    }
 #endif // IMPLEMENT_AABB == 1
    
    // T * R * I
    //const OrientationMatrix3<TYPENAME>& obbA_orientation( obbA.orientation ); //obbA.object->getOrientation() * obbA.raw_orientation );
    //const OrientationMatrix3<TYPENAME>& obbB_orientation( obbB.orientation ); //obbB.object->getOrientation() * obbB.raw_orientation );
    
    // The axes for obbA
    Vector3<TYPENAME> Ax( obbA.orientation.getXAxis() );
    Vector3<TYPENAME> Ay( obbA.orientation.getYAxis() );
    Vector3<TYPENAME> Az( obbA.orientation.getZAxis() );
    
    // The axes for obbB
    Vector3<TYPENAME> Bx( obbB.orientation.getXAxis() );
    Vector3<TYPENAME> By( obbB.orientation.getYAxis() );
    Vector3<TYPENAME> Bz( obbB.orientation.getZAxis() );
    
    // Rotation Matrix that rotates B relative to A's axes
    // I = transpose(A) * A
    // R = transpose(A) * B
    // Rij = Ai • Bj 
    OrientationMatrix3<TYPENAME> R( Ax*Bx, Ay*Bx, Az*Bx,
                                    Ax*By, Ay*By, Az*By,
                                    Ax*Bz, Ay*Bz, Az*Bz );
    
    const TYPENAME& WA( obbA.half_width );
    const TYPENAME& HA( obbA.half_height );
    const TYPENAME& DA( obbA.half_depth );
    const TYPENAME& WB( obbB.half_width );
    const TYPENAME& HB( obbB.half_height );
    const TYPENAME& DB( obbB.half_depth );
    
    // The 6 separating planes for the faces of the two boxes
    
    // Case 1:
    // L = Ax
    // | T*Ax | > WA + | WB * Rxx | + | HB * Rxy | + | DB * Rxz |
    if ( fabs( T*Ax ) > WA + fabs( WB*R.Xx ) + fabs( HB*R.Xy ) + fabs( DB*R.Xz ) )
    //{
        //printf("L = Ax - fabs( T*Ax ): %1f WA + fabs( WB*R.Xx ) + fabs( HB*R.Xy ) + fabs( DB*R.Xz ): %2f\n",
                //fabs( T*Ax ), WA + fabs( WB*R.Xx ) + fabs( HB*R.Xy ) + fabs( DB*R.Xz ));
        return false;
    //}
    
    // Case 2:
    // L = Ay
    // | T*Ay | > HA + | WB * Ryx | + | HB * Ryy | + | DB * Ryz |
    if ( fabs( T*Ay ) > HA + fabs( WB*R.Yx ) + fabs( HB*R.Yy ) + fabs( DB*R.Yz ) )
    //{
        //printf("L = Ay\n");
        return false;
    //}
    
    // Case 3:
    // L = Az
    // | T*Az | > DA + | WB * Rzx | + | HB * Rzy | + | DB * Rzz |
    if ( fabs( T*Az ) > DA + fabs( WB*R.Zx ) + fabs( HB*R.Zy ) + fabs( DB*R.Zz ) )
    //{
        //printf("L = Az\n");
        return false;
    //}
    
    // Case 4:
    // L = Bx
    // | T*Bx | > WB + | WA * Rxx | + | HA * Ryx | + | DA * Rzx |
    if ( fabs( T*Bx ) > WB + fabs( WA*R.Xx ) + fabs( HA*R.Yx ) + fabs( DA*R.Zx ) )
    //{
        //printf("L = Bx\n");
        return false;
    //}
    
    // Case 5:
    // L = By
    // | T*By | > HB + | WA * Rxy | + | HA * Ryy | + | DA * Rzy |
    if ( fabs( T*By ) > HB + fabs( WA*R.Xy ) + fabs( HA*R.Yy ) + fabs( DA*R.Zy ) )
    //{
        //printf("L = By\n");
        return false;
    //}
    
    // Case 6:
    // L = Bz
    // | T*Bz | > DB + | WA * Rxz | + | HA * Ryz | + | DA * Rzz |
    if ( fabs( T*Bz ) > DB + fabs( WA*R.Xz ) + fabs( HA*R.Yz ) + fabs( DA*R.Zz ) )
    //{
        //printf("L = Bz\n");
        return false;
    //}
    
    // The 9 separating planes for the edges of the two boxes
    
    // Case 7:
    // L = Ax x Bx
    // |(T*Az)*Ryx - (T*Ay)*Rzx| > |HA*Rzx| + |DA*Ryx| + |HB*Rxz| + |DB*Rxy|
    if ( fabs( (T*Az*R.Yx) - (T*Ay*R.Zx) ) > 
                            fabs( HA*R.Zx ) + fabs( DA*R.Yx ) + fabs( HB*R.Xz ) + fabs( DB*R.Xy ) )
    //{
        //printf("L = Ax x Bx\n");
        return false;
    //}
    
    // Case 8:
    // L = Ax x By
    // |(T*Az)*Ryy - (T*Ay)*Rzy| > |HA*Rzy| + |DA*Ryy| + |WB*Rxz| + |DB*Rxx|
    if ( fabs( (T*Az*R.Yy) - (T*Ay*R.Zy) ) > 
                            fabs( HA*R.Zy ) + fabs( DA*R.Yy ) + fabs( WB*R.Xz ) + fabs( DB*R.Xx ) )
    //{
        //printf("L = Ax x By\n");
        return false;
    //}
    
    // Case 9:
    // L = Ax x Bz
    // |(T*Az)*Ryz - (T*Ay)*Rzz| > |HA*Rzz| + |DA*Ryz| + |WB*Rxy| + |HB*Rxx|
    if ( fabs( (T*Az*R.Yz) - (T*Ay*R.Zz) ) > 
                            fabs( HA*R.Zz ) + fabs( DA*R.Yz ) + fabs( WB*R.Xy ) + fabs( HB*R.Xx ) )
    //{
        //printf("L = Ax x Bz\n");
        return false;
    //}
    
    // Case 10:
    // L = Ay x Bx
    // |(T*Ax)*Rzx - (T*Az)*Rxx| > |WA*Rzx| + |DA*Rxx| + |HB*Ryz| + |DB*Ryy|
    if ( fabs( (T*Ax*R.Zx) - (T*Az*R.Xx) ) > 
                            fabs( WA*R.Zx ) + fabs( DA*R.Xx ) + fabs( HB*R.Yz ) + fabs( DB*R.Yy ) )
    //{
        //printf("L = Ay x Bx\n");
        return false;
    //}
    
    // Case 11:
    // L = Ay x By
    // |(T*Ax)*Rzy - (T*Az)*Rxy| > |WA*Rzy| + |DA*Rxy| + |WB*Ryz| + |DB*Ryx|
    if ( fabs( (T*Ax*R.Zy) - (T*Az*R.Xy) ) > 
                            fabs( WA*R.Zy ) + fabs( DA*R.Xy ) + fabs( WB*R.Yz ) + fabs( DB*R.Yx ) )
    //{
        //printf("L = Ay x By\n");
        return false;
    //}
    
    // Case 12:
    // L = Ay x Bz
    // |(T*Ax)*Rzz - (T*Az)*Rxz| > |WA*Rzz| + |DA*Rxz| + |WB*Ryy| + |HB*Ryx|
    if ( fabs( (T*Ax*R.Zz) - (T*Az*R.Xz) ) > 
                            fabs( WA*R.Zz ) + fabs( DA*R.Xz ) + fabs( WB*R.Yy ) + fabs( HB*R.Yx ) )
    //{
        //printf("L = Ay x Bz\n");
        return false;
    //}
    
    // Case 13:
    // L = Az x Bx
    // |(T*Ay)*Rxx - (T*Ax)*Ryx| > |WA*Ryx| + |HA*Rxx| + |HB*Rzz| + |DB*Rzy|
    if ( fabs( (T*Ay*R.Xx) - (T*Ax*R.Yx) ) > 
                            fabs( WA*R.Yx ) + fabs( HA*R.Xx ) + fabs( HB*R.Zz ) + fabs( DB*R.Zy ) )
    //{
        //printf("L = Az x Bx\n");
        return false;
    //}
    
    // Case 14:
    // L = Az x By
    // |(T*Ay)*Rxy - (T*Ax)*Ryy| > |WA*Ryy| + |HA*Rxy| + |WB*Rzz| + |DB*Rzx|
    if ( fabs( (T*Ay*R.Xy) - (T*Ax*R.Yy) ) > 
                            fabs( WA*R.Yy ) + fabs( HA*R.Xy ) + fabs( WB*R.Zz ) + fabs( DB*R.Zx ) )
    //{
        //printf("L = Az x By\n");
        return false;
    //}
    
    // Case 15:
    // L = Az x Bz
    // |(T*Ay)*Rxz - (T*Ax)*Ryz| > |WA*Ryz| + |HA*Rxz| + |WB*Rzy| + |HB*Rzx|
    if ( fabs( (T*Ay*R.Xz) - (T*Ax*R.Yz) ) > 
                            fabs( WA*R.Yz ) + fabs( HA*R.Xz ) + fabs( WB*R.Zy ) + fabs( HB*R.Zx ) )
    //{
        //printf("L = Az x Bz\n");
        return false;
    //}
    //printf("True (OBB.h)\n");
    return true;
 }
 #endif // GL_OBJECT_COLLISION_DETECTION_TYPE == 1
 
 #if IMPLEMENT_INTERPOLATION == 1
 /**
  * collisionExtent() returns the extent that the OBBs need to move in the directions
  * toward their previous positions to avoid the collision; if the extent returned is less
  * than zero, then the two OBBs are currently not intersecting. If the two OBBs are 
  * colliding, but the difference between the specified displacementA and displacementB is 
  * the zero vector (i.e. displacementA == displacementB), then the displacementA and 
  * displacementB will be modified to be directed towards the centers of each other's OBBs;
  * if the centers of the OBBs are at the same location, then the displacements will be 
  * modified to go in separate directions of the normal of the one of the faces of the OBB
  * with the least overlap between the two OBBs.
  *
  * @param (OBB<TYPENAME>&) obbA - an OBB of object A
  * @param (OBB<TYPENAME>&) obbB - an OBB of object B
  * @param (Vector3<TYPENAME>&) obbA_displacement - the current position of obbA minus the previous position of obbA
  * @param (Vector3<TYPENAME>&) obbB_displacement - the current position of obbB minus the previous position of obbB
  * @return TYPENAME
  */
 template< typename TYPENAME >
 inline TYPENAME collisionExtent( OBB<TYPENAME>& obbA, OBB<TYPENAME>& obbB, 
                                    Vector3<TYPENAME>& obbA_displacement, Vector3<TYPENAME>& obbB_displacement )

 {
 #if IMPLEMENT_BOUNDING_SPHERE == 1
 #if GL_OBJECT_COLLISION_DETECTION_TYPE == 1
     
    // Additional performance may be gained by separating isMaintained into isCenterMaintained and 
    // isOrientationMaintained, but the gain is not certain because an object that requires maintainence
    // in the first place, will likely be a mobile object that moves often.
    if ( !obbA.isMaintained )
        obbA.center = (obbA.object->getOrientation() * obbA.raw_center) + obbA.object->getPosition();
    if ( !obbB.isMaintained )
        obbB.center = (obbB.object->getOrientation() * obbB.raw_center) + obbB.object->getPosition();
 
 #endif // if GL_OBJECT_COLLISION_DETECTION_TYPE == 1   
 #endif // IMPLEMENT_BOUNDING_SPHERE == 1
    
    // Translation vector that moves the position of B relative to the position of A
    // (0, 0, 0) = PA - PA
    // T = PB – PA
    Vector3<TYPENAME> T( obbB.center - obbA.center );
    
 #if IMPLEMENT_BOUNDING_SPHERE == 1
    
    if (global::enableBoundingSphere)
    {
        if ( magnitude_squared( T ) > (obbA.radius + obbB.radius) * (obbA.radius + obbB.radius) )
            return false;
    }
    
 #if GL_OBJECT_COLLISION_DETECTION_TYPE == 1  
    if ( !obbA.isMaintained )
    {
        obbA.orientation = obbA.object->getOrientation() * obbA.raw_orientation;
        obbA.isMaintained = true;
    }
    if ( !obbB.isMaintained )
    {
        obbB.orientation = obbB.object->getOrientation() * obbB.raw_orientation;
        obbB.isMaintained = true;
    }
 #endif // GL_OBJECT_COLLISION_DETECTION_TYPE == 1       
 #endif // IMPLEMENT_BOUNDING_SPHERE == 1
 
 #if IMPLEMENT_BOUNDING_SPHERE == 0
 #if GL_OBJECT_COLLISION_DETECTION_TYPE == 1
    if ( !obbA.isMaintained )
        maintain( obbA );
    if ( !obbB.isMaintained )
        maintain( obbB );
 #endif // GL_OBJECT_COLLISION_DETECTION_TYPE == 1
 #endif // IMPLEMENT_BOUNDING_SPHERE == 0
        
 #if IMPLEMENT_AABB == 1
     if (global::enableAABB)
     {
        if ( fabs(T.x) > (obbA.half_width * fabs(obbA.orientation.Xx)) + (obbA.half_height * fabs(obbA.orientation.Yx)) 
                       + (obbA.half_depth * fabs(obbA.orientation.Zx)) 
                       + (obbB.half_width * fabs(obbB.orientation.Xx)) + (obbB.half_height * fabs(obbB.orientation.Yx)) 
                       + (obbB.half_depth * fabs(obbB.orientation.Zx)) )
                        return false;
                        
        if ( fabs(T.y) > (obbA.half_width * fabs(obbA.orientation.Xy)) + (obbA.half_height * fabs(obbA.orientation.Yy)) 
                       + (obbA.half_depth * fabs(obbA.orientation.Zy)) 
                       + (obbB.half_width * fabs(obbB.orientation.Xy)) + (obbB.half_height * fabs(obbB.orientation.Yy)) 
                       + (obbB.half_depth * fabs(obbB.orientation.Zy)) )
                        return false;
                        
        if ( fabs(T.z) > (obbA.half_width * fabs(obbA.orientation.Xz)) + (obbA.half_height * fabs(obbA.orientation.Yz)) 
                       + (obbA.half_depth * fabs(obbA.orientation.Zz))
                       + (obbB.half_width * fabs(obbB.orientation.Xz)) + (obbB.half_height * fabs(obbB.orientation.Yz)) 
                       + (obbB.half_depth * fabs(obbB.orientation.Zz)) )
                        return false;
     }
 #endif // IMPLEMENT_AABB == 1
    
    // The axes for obbA
    Vector3<TYPENAME> Ax( obbA.orientation.getXAxis() );
    Vector3<TYPENAME> Ay( obbA.orientation.getYAxis() );
    Vector3<TYPENAME> Az( obbA.orientation.getZAxis() );
    
    // The axes for obbB
    Vector3<TYPENAME> Bx( obbB.orientation.getXAxis() );
    Vector3<TYPENAME> By( obbB.orientation.getYAxis() );
    Vector3<TYPENAME> Bz( obbB.orientation.getZAxis() );
    
    // Rotation Matrix that rotates B relative to A's axes
    // I = transpose(A) * A
    // R = transpose(A) * B
    // Rij = Ai • Bj 
    OrientationMatrix3<TYPENAME> R( Ax*Bx, Ay*Bx, Az*Bx,
                                    Ax*By, Ay*By, Az*By,
                                    Ax*Bz, Ay*Bz, Az*Bz );
    
    const TYPENAME& WA( obbA.half_width );
    const TYPENAME& HA( obbA.half_height );
    const TYPENAME& DA( obbA.half_depth );
    const TYPENAME& WB( obbB.half_width );
    const TYPENAME& HB( obbB.half_height );
    const TYPENAME& DB( obbB.half_depth );
    
    // change the displacements if the displacements are equal
    if ( obbA_displacement == obbB_displacement )
    {
        // if the centers of both OBBs are at the same location
        if ( T.isZeroVector() )
        {
            // L = Ax
            Vector3<TYPENAME> min_axis( Ax );
            // overlapping extent in the separating axis direction
            TYPENAME min_overlap_extent( (WA + fabs( WB*R.Xx ) + fabs( HB*R.Xy ) + fabs( DB*R.Xz )) - fabs( T*Ax ) );
            
            if ( min_overlap_extent < 0.0f )
                return -1.0f;
            
            TYPENAME temp_overlap_extent( (HA + fabs( WB*R.Yx ) + fabs( HB*R.Yy ) + fabs( DB*R.Yz )) - fabs( T*Ay ) );
            
            // L = Ay
            if ( temp_overlap_extent < 0.0f )
                return -1.0f;
            else if ( temp_overlap_extent < min_overlap_extent )
            {
                min_axis = Ay;
                min_overlap_extent = temp_overlap_extent;
            }
            
            // L = Az
            temp_overlap_extent = (DA + fabs( WB*R.Zx ) + fabs( HB*R.Zy ) + fabs( DB*R.Zz )) - fabs( T*Az );
            if ( temp_overlap_extent < 0.0f )
                return -1.0f;
            else if ( temp_overlap_extent < min_overlap_extent )
            {
                min_axis = Az;
                min_overlap_extent = temp_overlap_extent;
            }
            
            // L = Bx
            temp_overlap_extent = (WB + fabs( WA*R.Xx ) + fabs( HA*R.Yx ) + fabs( DA*R.Zx )) - fabs( T*Bx );
            if ( temp_overlap_extent < 0.0f )
                return -1.0f;
            else if ( temp_overlap_extent < min_overlap_extent )
            {
                min_axis = Bx;
                min_overlap_extent = temp_overlap_extent;
            }
            
            // L = By
            temp_overlap_extent = (HB + fabs( WA*R.Xy ) + fabs( HA*R.Yy ) + fabs( DA*R.Zy )) - fabs( T*By );
            if ( temp_overlap_extent < 0.0f )
                return -1.0f;
            else if ( temp_overlap_extent < min_overlap_extent )
            {
                min_axis = By;
                min_overlap_extent = temp_overlap_extent;
            }
            
            // L = Bz
            temp_overlap_extent = (DB + fabs( WA*R.Xz ) + fabs( HA*R.Yz ) + fabs( DA*R.Zz )) - fabs( T*Bz );
            if ( temp_overlap_extent < 0.0f )
                return -1.0f;
            else if ( temp_overlap_extent < min_overlap_extent )
            {
                min_axis = Bz;
                min_overlap_extent = temp_overlap_extent;
            }
            
            // Set the displacements to be directed towards the normal of a face of an OBB that
            // has the minimum overlap between the two OBBs
            if ( T * min_axis > 0.0f )
            {
                obbA_displacement = min_axis;
                obbB_displacement = -min_axis;
            }
            else
            {
                obbA_displacement = -min_axis;
                obbB_displacement = min_axis;
            }
        }
        else
        {
            // Set the displacements of the OBBs to be directed towards one another
            obbA_displacement = T;
            obbB_displacement = -T;
        }
    }
    
    // netVelocity = obbA_displacement - obbB_displacement = netVelocityMagnitude * netUnitVelocity
    Vector3<TYPENAME> netUnitVelocity( normal( obbA_displacement - obbB_displacement ) );
    
    // overlapping extent in the direction of the net velocity
    // overlapping_length / | separating_axis * netUnitVelocity |
    TYPENAME min_overlapping_extent( std::numeric_limits<float>::max() );
    
    // The 6 separating planes for the faces of the two boxes
    
    // The two obbs overlap past half way if the dot product between (previousPositionB - previousPositionA) 
    // and (currentPositionB - previousPositionA) is less than zero
    // obbA.previousCenter = obbA.currentCenter - displacementA
    // obbB.previousCenter = obbB.currentCenter - displacementB
    // ( (obbB.previousCenter - obbA.previousCenter) * (obbB.currentCenter - obbA.currentCenter) < 0.0f )
    //if ( (T - displacementB + displacementA) * T < 0.0f )
    //{
    /*Vector3<TYPENAME> obbA_previousProjection( (obbA.center - displacementA) * Ax );
    Vector3<TYPENAME> obbA_currentProjection( obbA.center * Ax );
    Vector3<TYPENAME> obbB_previousProjection( (obbB.center - displacementB) * Ax );
    Vector3<TYPENAME> obbB_currentProjection( obbB.center * Ax );*/
    /*TYPENAME currentCenter_ProjectionA( (obbA.center - obbB.center) * Ax );
    if ( (displacementA * Ax) * currentCenter_ProjectionA > ZERO
        || (displacementB * Ax) * currentCenter_ProjectionA < ZERO )*/
    /*if ( (displacementA * Ax) * ((obbA.center - obbB.center) * Ax) > ZERO
        || (displacementB * Ax) * ((obbB.center - obbA.center) * Ax) > ZERO )*/
    /*if ( ((T - displacementB + displacementA) * Ax) * (T * Ax) < ZERO 
        || (displacementA * Ax) * ((obbA.center - obbB.center) * Ax) > ZERO
        || (displacementB * Ax) * ((obbB.center - obbA.center) * Ax) > ZERO )*/
    /*if ( ((T - displacementB + displacementA) * Ax) * (T * Ax) < ZERO 
        || ((obbA.center - (obbA.center - displacementA)) * Ax) * ((obbA.center - obbB.center) * Ax) > ZERO
        || ((obbB.center - (obbB.center - displacementB)) * Ax) * ((obbB.center - obbA.center) * Ax) > ZERO )*/
    /*if ( (obbB_currentProjection - obbA_currentProjection) * (obbB_previousProjection - obbA_previousProjection) < ZERO 
        || (obbA_currentProjection - obbA_previousProjection) * (obbA_currentProjection - obbB_currentProjection) > ZERO
        || (obbB_currentProjection - obbB_previousProjection) * (obbB_currentProjection - obbA_currentProjection) > ZERO )*/
    // The two OBBs overlap past half way if 
    // (obbA_currentProjection - obbA_previousProjection) * (obbA_currentProjection - obbB_currentProjection) > ZERO
    // or
    // (obbB_currentProjection - obbB_previousProjection) * (obbB_currentProjection - obbA_currentProjection) > ZERO

    // Case 1:
    // L = Ax
    // | T*Ax | > WA + | WB * Rxx | + | HB * Rxy | + | DB * Rxz |
    TYPENAME half_extent_of_both_OBB( (WA + fabs( WB*R.Xx ) + fabs( HB*R.Xy ) + fabs( DB*R.Xz )) );
    // overlapping_length is the overlapping magnitude/length between the two OBBs
    TYPENAME overlapping_length( collisionExtent( obbA, obbB, obbA_displacement, obbB_displacement, 
                                             Ax, half_extent_of_both_OBB, T, netUnitVelocity ) );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_extent )
            min_overlapping_extent = overlapping_length;
    }
    else
        return -1.0f;
    
    // Case 2:
    // L = Ay
    // | T*Ay | > HA + | WB * Ryx | + | HB * Ryy | + | DB * Ryz |
    half_extent_of_both_OBB = (HA + fabs( WB*R.Yx ) + fabs( HB*R.Yy ) + fabs( DB*R.Yz ));
    overlapping_length = collisionExtent( obbA, obbB, obbA_displacement, obbB_displacement, 
                                             Ay, half_extent_of_both_OBB, T, netUnitVelocity );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_extent )
            min_overlapping_extent = overlapping_length;
    }
    else
        return -1.0f;
    
    // Case 3:
    // L = Az
    // | T*Az | > DA + | WB * Rzx | + | HB * Rzy | + | DB * Rzz |
    half_extent_of_both_OBB = (DA + fabs( WB*R.Zx ) + fabs( HB*R.Zy ) + fabs( DB*R.Zz ));
    overlapping_length = collisionExtent( obbA, obbB, obbA_displacement, obbB_displacement, 
                                             Az, half_extent_of_both_OBB, T, netUnitVelocity );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_extent )
            min_overlapping_extent = overlapping_length;
    }
    else
        return -1.0f;
    
    // Case 4:
    // L = Bx
    // | T*Bx | > WB + | WA * Rxx | + | HA * Ryx | + | DA * Rzx |
    half_extent_of_both_OBB = (WB + fabs( WA*R.Xx ) + fabs( HA*R.Yx ) + fabs( DA*R.Zx ));
    overlapping_length = collisionExtent( obbA, obbB, obbA_displacement, obbB_displacement, 
                                             Bx, half_extent_of_both_OBB, T, netUnitVelocity );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_extent )
            min_overlapping_extent = overlapping_length;
    }
    else
        return -1.0f;
    
    // Case 5:
    // L = By
    // | T*By | > HB + | WA * Rxy | + | HA * Ryy | + | DA * Rzy |
    half_extent_of_both_OBB = (HB + fabs( WA*R.Xy ) + fabs( HA*R.Yy ) + fabs( DA*R.Zy ));
    overlapping_length = collisionExtent( obbA, obbB, obbA_displacement, obbB_displacement, 
                                             By, half_extent_of_both_OBB, T, netUnitVelocity );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_extent )
            min_overlapping_extent = overlapping_length;
    }
    else
        return -1.0f;
    
    // Case 6:
    // L = Bz
    // | T*Bz | > DB + | WA * Rxz | + | HA * Ryz | + | DA * Rzz |
    half_extent_of_both_OBB = (DB + fabs( WA*R.Xz ) + fabs( HA*R.Yz ) + fabs( DA*R.Zz ));
    overlapping_length = collisionExtent( obbA, obbB, obbA_displacement, obbB_displacement, 
                                             Bz, half_extent_of_both_OBB, T, netUnitVelocity );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_extent )
            min_overlapping_extent = overlapping_length;
    }
    else
        return -1.0f;
    
    // The 9 separating planes for the edges of the two boxes
    
    // Case 7:
    // L = Ax x Bx
    // |(T*Az)*Ryx - (T*Ay)*Rzx| > |HA*Rzx| + |DA*Ryx| + |HB*Rxz| + |DB*Rxy|
    Vector3<TYPENAME> L( crossProduct( Ax, Bx ) );
    half_extent_of_both_OBB = (fabs( HA*R.Zx ) + fabs( DA*R.Yx ) + fabs( HB*R.Xz ) + fabs( DB*R.Xy ));
    overlapping_length = collisionExtent( obbA, obbB, obbA_displacement, obbB_displacement, 
                                             L, half_extent_of_both_OBB, T, netUnitVelocity );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_extent )
            min_overlapping_extent = overlapping_length;
    }
    else
        return -1.0f;
    
    // Case 8:
    // L = Ax x By
    // |(T*Az)*Ryy - (T*Ay)*Rzy| > |HA*Rzy| + |DA*Ryy| + |WB*Rxz| + |DB*Rxx|
    L = crossProduct( Ax, By );
    half_extent_of_both_OBB = (fabs( HA*R.Zy ) + fabs( DA*R.Yy ) + fabs( WB*R.Xz ) + fabs( DB*R.Xx ));
    overlapping_length = collisionExtent( obbA, obbB, obbA_displacement, obbB_displacement, 
                                             L, half_extent_of_both_OBB, T, netUnitVelocity );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_extent )
            min_overlapping_extent = overlapping_length;
    }
    else
        return -1.0f;
    
    // Case 9:
    // L = Ax x Bz
    // |(T*Az)*Ryz - (T*Ay)*Rzz| > |HA*Rzz| + |DA*Ryz| + |WB*Rxy| + |HB*Rxx|
    L = crossProduct( Ax, Bz );
    half_extent_of_both_OBB = (fabs( HA*R.Zz ) + fabs( DA*R.Yz ) + fabs( WB*R.Xy ) + fabs( HB*R.Xx ));
    overlapping_length = collisionExtent( obbA, obbB, obbA_displacement, obbB_displacement, 
                                             L, half_extent_of_both_OBB, T, netUnitVelocity );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_extent )
            min_overlapping_extent = overlapping_length;
    }
    else
        return -1.0f;
    
    // Case 10:
    // L = Ay x Bx
    // |(T*Ax)*Rzx - (T*Az)*Rxx| > |WA*Rzx| + |DA*Rxx| + |HB*Ryz| + |DB*Ryy|
    L = crossProduct( Ay, Bx );
    half_extent_of_both_OBB = (fabs( WA*R.Zx ) + fabs( DA*R.Xx ) + fabs( HB*R.Yz ) + fabs( DB*R.Yy ));
    overlapping_length = collisionExtent( obbA, obbB, obbA_displacement, obbB_displacement, 
                                             L, half_extent_of_both_OBB, T, netUnitVelocity );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_extent )
            min_overlapping_extent = overlapping_length;
    }
    else
        return -1.0f;
    
    // Case 11:
    // L = Ay x By
    // |(T*Ax)*Rzy - (T*Az)*Rxy| > |WA*Rzy| + |DA*Rxy| + |WB*Ryz| + |DB*Ryx|
    L = crossProduct( Ay, By );
    half_extent_of_both_OBB = (fabs( WA*R.Zy ) + fabs( DA*R.Xy ) + fabs( WB*R.Yz ) + fabs( DB*R.Yx ));
    overlapping_length = collisionExtent( obbA, obbB, obbA_displacement, obbB_displacement, 
                                             L, half_extent_of_both_OBB, T, netUnitVelocity );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_extent )
            min_overlapping_extent = overlapping_length;
    }
    else
        return -1.0f;
    
    // Case 12:
    // L = Ay x Bz
    // |(T*Ax)*Rzz - (T*Az)*Rxz| > |WA*Rzz| + |DA*Rxz| + |WB*Ryy| + |HB*Ryx|
    L = crossProduct( Ay, Bz );
    half_extent_of_both_OBB = (fabs( WA*R.Zz ) + fabs( DA*R.Xz ) + fabs( WB*R.Yy ) + fabs( HB*R.Yx ));
    overlapping_length = collisionExtent( obbA, obbB, obbA_displacement, obbB_displacement, 
                                             L, half_extent_of_both_OBB, T, netUnitVelocity );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_extent )
            min_overlapping_extent = overlapping_length;
    }
    else
        return -1.0f;
    
    // Case 13:
    // L = Az x Bx
    // |(T*Ay)*Rxx - (T*Ax)*Ryx| > |WA*Ryx| + |HA*Rxx| + |HB*Rzz| + |DB*Rzy|
    L = crossProduct( Az, Bx );
    half_extent_of_both_OBB = (fabs( WA*R.Yx ) + fabs( HA*R.Xx ) + fabs( HB*R.Zz ) + fabs( DB*R.Zy ));
    overlapping_length = collisionExtent( obbA, obbB, obbA_displacement, obbB_displacement, 
                                             L, half_extent_of_both_OBB, T, netUnitVelocity );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_extent )
            min_overlapping_extent = overlapping_length;
    }
    else
        return -1.0f;
    
    // Case 14:
    // L = Az x By
    // |(T*Ay)*Rxy - (T*Ax)*Ryy| > |WA*Ryy| + |HA*Rxy| + |WB*Rzz| + |DB*Rzx|
    L = crossProduct( Az, By );
    half_extent_of_both_OBB = (fabs( WA*R.Yy ) + fabs( HA*R.Xy ) + fabs( WB*R.Zz ) + fabs( DB*R.Zx ));
    overlapping_length = collisionExtent( obbA, obbB, obbA_displacement, obbB_displacement, 
                                             L, half_extent_of_both_OBB, T, netUnitVelocity );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_extent )
            min_overlapping_extent = overlapping_length;
    }
    else
        return -1.0f;
    
    
    // Case 15:
    // L = Az x Bz
    // |(T*Ay)*Rxz - (T*Ax)*Ryz| > |WA*Ryz| + |HA*Rxz| + |WB*Rzy| + |HB*Rzx|
    L = crossProduct( Az, Bz );
    half_extent_of_both_OBB = (fabs( WA*R.Yz ) + fabs( HA*R.Xz ) + fabs( WB*R.Zy ) + fabs( HB*R.Zx ));
    overlapping_length = collisionExtent( obbA, obbB, obbA_displacement, obbB_displacement, 
                                             L, half_extent_of_both_OBB, T, netUnitVelocity );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_extent )
            min_overlapping_extent = overlapping_length;
    }
    else
        return -1.0f;
    
    /*
    // Case 15:
    // L = Az x Bz
    // |(T*Ay)*Rxz - (T*Ax)*Ryz| > |WA*Ryz| + |HA*Rxz| + |WB*Rzy| + |HB*Rzx|
    half_extent_of_both_OBB = (fabs( WA*R.Yz ) + fabs( HA*R.Xz ) + fabs( WB*R.Zy ) + fabs( HB*R.Zx ));
    overlapping_length = half_extent_of_both_OBB - fabs( (T*Ay*R.Xz) - (T*Ax*R.Yz) );
    if ( overlapping_length >= 0.0f )
    {
        const Vector3<TYPENAME> L( normal( crossProduct( Az, Bz ) ) );
        
        
        TYPENAME projection( fabs(netUnitVelocity * L) );
        if ( projection == ZERO )
            projection = EPSILON;
        
        const TYPENAME currentCenter_ProjectionA( (obbA.center - obbB.center) * L );
        TYPENAME displacementA_Projection( displacementA * L );
        TYPENAME displacementB_Projection( displacementB * L );
        const bool obbA_past_half_way( displacementA_Projection * currentCenter_ProjectionA > ZERO );
        const bool obbB_past_half_way( displacementB_Projection * currentCenter_ProjectionA < ZERO );
        if ( displacementA_Projection < ZERO )
            displacementA_Projection = fabs( displacementA_Projection );
        if ( displacementB_Projection < ZERO )
            displacementB_Projection = fabs( displacementB_Projection );
        if ( obbA_past_half_way )
        {
            // obbA_past_half_way && obbB_past_half_way
            if ( obbB_past_half_way || ( displacementA_Projection > displacementB_Projection ) )
                overlapping_length = ((2.0f * half_extent_of_both_OBB) - overlapping_length) / projection;
            else // obbA_past_half_way && !obbB_past_half_way
                overlapping_length /= projection;
        }
        else // !obbA_past_half_way
        {
            // !obbA_past_half_way && obbB_past_half_way
            if ( obbB_past_half_way && ( displacementB_Projection > displacementA_Projection ) )
                overlapping_length = ((2.0f * half_extent_of_both_OBB) - overlapping_length) / projection;
            else // !obbA_past_half_way && !obbB_past_half_way
                overlapping_length /= projection;
        }
        
        if ( overlapping_length < min_overlapping_extent )
            min_overlapping_extent = overlapping_length;
    }
    else
        return -1.0f;
    */
    
    return min_overlapping_extent;
 }
 
 /**
  * collisionExtent() returns the extent the projection of the two specified OBBs onto the
  * specified axis intersect. The two specified OBBs do not intersect if the entent is less than zero. 
  * The axis does not have to be a unit vector. The axis should be the same vector used to compute the
  * half_extent_of_both_OBB.
  *
  * @param (const OBB<TYPENAME>&) obbA - OBB at its current position
  * @param (const OBB<TYPENAME>&) obbB - OBB at its current position
  * @param (const Vector3<TYPENAME>&) obbA_displacement - the current position of obbA minus the previous position of obbA
  * @param (const Vector3<TYPENAME>&) obbB_displacement - the current position of obbB minus the previous position of obbB
  * @param (const Vector3<TYPENAME>&) axis - the separating axis being checked against
  * @param (const TYPENAME) half_extent_of_both_OBB - the projection of the both OBBs onto the specified axis, divided by two
  * @param (const Vector3<TYPENAME>&) T - the center of obbB minus the center of obbA
  * @param (const Vector3<TYPENAME>&) netUnitVelocity - the velocity of obbA minus the velocity of obbB (or the other way 
  *                                                     around), normalized into a unit vector
  * @return bool
  */
 template <typename TYPENAME> 
 inline TYPENAME collisionExtent( const OBB<TYPENAME>& obbA, const OBB<TYPENAME>& obbB, 
        const Vector3<TYPENAME>& obbA_displacement, const Vector3<TYPENAME>& obbB_displacement, const Vector3<TYPENAME>& axis,        const TYPENAME half_extent_of_both_OBB, const Vector3<TYPENAME>& T, const Vector3<TYPENAME>& netUnitVelocity )
 {
    // T = (obbB.center - obbA.center)
    // T_projection = (obbB.center - obbA.center) * axis
    const TYPENAME T_projection( T * axis );
    
    // overlapping_length is the overlapping magnitude/length between the two OBBs
    // TYPENAME overlapping_length( half_extent_of_both_OBB - fabs( T*axis ) );
    TYPENAME overlapping_length( half_extent_of_both_OBB - fabs( T_projection ) );
    
    // L = axis
    if ( overlapping_length >= 0.0f )
    {
        TYPENAME projection( fabs(netUnitVelocity * axis) );
        if ( projection == ZERO )
            return std::numeric_limits<float>::max();
            //projection = EPSILON;
        
        TYPENAME obbA_displacement_projection( obbA_displacement * axis );
        TYPENAME obbB_displacement_projection( obbB_displacement * axis );
        const bool obbA_past_half_way( obbA_displacement_projection * T_projection < ZERO );
        const bool obbB_past_half_way( obbB_displacement_projection * T_projection > ZERO );
        if ( obbA_displacement_projection < ZERO )
            obbA_displacement_projection = fabs( obbA_displacement_projection );
        if ( obbB_displacement_projection < ZERO )
            obbB_displacement_projection = fabs( obbB_displacement_projection );
        if ( obbA_past_half_way )
        {
            // obbA_past_half_way && (obbB_past_half_way || obbA_displacement_projection > obbB_displacement_projection )
            if ( obbB_past_half_way || ( obbA_displacement_projection > obbB_displacement_projection ) )
                overlapping_length = ((2.0f * half_extent_of_both_OBB) - overlapping_length) / projection;
            else // obbA_past_half_way && !obbB_past_half_way && obbA_displacement_projection <= obbB_displacement_projection
                overlapping_length /= projection;
        }
        else // !obbA_past_half_way
        {
            // !obbA_past_half_way && obbB_past_half_way && obbB_displacement_projection > obbA_displacement_projection
            if ( obbB_past_half_way && ( obbB_displacement_projection > obbA_displacement_projection ) )
                overlapping_length = ((2.0f * half_extent_of_both_OBB) - overlapping_length) / projection;
            else // !obbA_past_half_way && !obbB_past_half_way && obbB_displacement_projection <= obbA_displacement_projection
                overlapping_length /= projection;
        }
 
 //#define DEBUG_COLLISION_EXTENT
 #ifdef DEBUG_COLLISION_EXTENT       
        // TEST
        {
            Vector3<TYPENAME> L( axis );
            TYPENAME overlap( half_extent_of_both_OBB - fabs( T*L ) );
            TYPENAME projection( fabs(netUnitVelocity * L) );
            if ( projection == ZERO )
                projection = EPSILON;
            
            
            // overlapping_length = (- overlapping_length) - fabs(extent_of_both_OBB / L);
            TYPENAME overlapping_length2;
            
            Vector3<TYPENAME> obbA_previousProjection( (obbA.center - obbA_displacement) * L );
            Vector3<TYPENAME> obbA_currentProjection( obbA.center * L );
            Vector3<TYPENAME> obbB_previousProjection( (obbB.center - obbB_displacement) * L );
            Vector3<TYPENAME> obbB_currentProjection( obbB.center * L );
            
            TYPENAME currentCenter_ProjectionA( (obbA.center - obbB.center) * L );
            TYPENAME displacementA_Projection( obbA_displacement * L );
            TYPENAME displacementB_Projection( obbB_displacement * L ); 
            const bool obbA_past_half_way( displacementA_Projection * currentCenter_ProjectionA > ZERO );
            const bool obbB_past_half_way( displacementB_Projection * currentCenter_ProjectionA < ZERO );
            if ( displacementA_Projection < ZERO )
                displacementA_Projection = fabs( displacementA_Projection );
            if ( displacementB_Projection < ZERO )
                displacementB_Projection = fabs( displacementB_Projection );
            if ( obbA_past_half_way )
            {
                // obbA_past_half_way && obbB_past_half_way
                if ( obbB_past_half_way || ( displacementA_Projection > displacementB_Projection ) )
                    overlapping_length2 = ((2.0f * half_extent_of_both_OBB) - overlap) / projection;
                else // obbA_past_half_way && !obbB_past_half_way
                    overlapping_length2 = overlap / projection;
            }
            else // !obbA_past_half_way
            {
                // !obbA_past_half_way && obbB_past_half_way
                if ( obbB_past_half_way && ( displacementB_Projection > displacementA_Projection ) )
                    overlapping_length2 = ((2.0f * half_extent_of_both_OBB) - overlap) / projection;
                else // !obbA_past_half_way && !obbB_past_half_way
                    overlapping_length2 = overlap / projection;
            }
 #if 0
            if ( (displacementA * Ax) * currentCenter_ProjectionA > ZERO
                || (displacementB * Ax) * currentCenter_ProjectionA < ZERO )
            /*if ( (displacementA * Ax) * ((obbA.center - obbB.center) * Ax) > ZERO
                || (displacementB * Ax) * ((obbB.center - obbA.center) * Ax) > ZERO )*/
            /*if ( ((T - displacementB + displacementA) * Ax) * (T * Ax) < ZERO 
                || (displacementA * Ax) * ((obbA.center - obbB.center) * Ax) > ZERO
                || (displacementB * Ax) * ((obbB.center - obbA.center) * Ax) > ZERO )*/
            /*if ( ((T - displacementB + displacementA) * Ax) * (T * Ax) < ZERO 
                || ((obbA.center - (obbA.center - displacementA)) * Ax) * ((obbA.center - obbB.center) * Ax) > ZERO
                || ((obbB.center - (obbB.center - displacementB)) * Ax) * ((obbB.center - obbA.center) * Ax) > ZERO )*/
            /*if ( (obbB_currentProjection - obbA_currentProjection) * (obbB_previousProjection - obbA_previousProjection) < ZERO 
                || (obbA_currentProjection - obbA_previousProjection) * (obbA_currentProjection - obbB_currentProjection) > ZERO
                || (obbB_currentProjection - obbB_previousProjection) * (obbB_currentProjection - obbA_currentProjection) > ZERO )*/
                overlapping_length2 = ((2.0f * half_extent_of_both_OBB) - overlap) / projection;
            else
                overlapping_length2 = overlap / projection;
 #endif // #if 0
            GL_MobileObject<TYPENAME> objectA( *obbA.object, obbA.object->getPosition() );
            GL_MobileObject<TYPENAME> objectB( *obbB.object, obbB.object->getPosition() );
            OBB<TYPENAME> obbA2( obbA, &objectA );
            OBB<TYPENAME> obbB2( obbB, &objectB );
            
            Vector3<TYPENAME> testPositionA( objectA.getPosition() );
            Vector3<TYPENAME> testPositionB( objectB.getPosition() );
            
            TYPENAME reverse_displacement_extent( 
                    (overlapping_length2 / magnitude( obbA_displacement - obbB_displacement ) ) + EPSILON2 );
            objectA.translate( reverse_displacement_extent * -obbA_displacement );
            objectB.translate( reverse_displacement_extent * -obbB_displacement );
            maintain( obbA2 );
            maintain( obbB2 );
            
            Vector3<TYPENAME> previousPositionA( obbA.center );
            Vector3<TYPENAME> previousPositionB( obbB.center );
            Vector3<TYPENAME> positionA( obbA2.center );
            Vector3<TYPENAME> positionB( obbB2.center );
            
            Vector3<TYPENAME> T2( positionB - positionA );
            TYPENAME new_overlap( half_extent_of_both_OBB - fabs( T2*L ) );
            
            bool hasCollided( collide( obbA2, obbB2 ) );
            if ( hasCollided )
            {
                Vector3<TYPENAME> displacementA2( previousPositionA - positionA );
                Vector3<TYPENAME> displacementB2( previousPositionB - positionB );
                TYPENAME new_extent( collisionExtent( obbA2, obbB2, displacementA2, displacementB2 ) );
                collide( obbA2, obbB2 );
                printf("Error\n");
            }
        }
#endif // #ifdef DEBUG_COLLISION_EXTENT 
        
        return overlapping_length;
    }
    else
        return -1.0f;
 }
 
 // Interpolation method projecting each point of the boxes individually
 #if 0
 /**
  * collisionExtent() returns the extent that the OBBs need to move in the directions
  * toward their previous positions to avoid the collision; if the extent returned is less
  * than zero, then the two OBBs are currently not intersecting. If the two OBBs are 
  * colliding, but the difference between the specified displacementA and displacementB is 
  * the zero vector (i.e. displacementA == displacementB), then the displacementA and 
  * displacementB will be modified to be directed towards the centers of each other's OBBs;
  * if the centers of the OBBs are at the same location, then the displacements will be 
  * modified to go in separate directions of the normal of the one of the faces of the OBB
  * with the least overlap between the two OBBs.
  *
  * @param (OBB<TYPENAME>&) obbA - an OBB of object A
  * @param (OBB<TYPENAME>&) obbB - an OBB of object B
  * @param (Vector3<TYPENAME>&) displacementA - the current position of obbA minus the previous position of obbA
  * @param (Vector3<TYPENAME>&) displacementB - the current position of obbB minus the previous position of obbB
  * @return TYPENAME
  */
 template< typename TYPENAME >
 inline TYPENAME collisionExtent( OBB<TYPENAME>& obbA, OBB<TYPENAME>& obbB, 
                                    Vector3<TYPENAME>& displacementA, Vector3<TYPENAME>& displacementB )

 {
 #if IMPLEMENT_BOUNDING_SPHERE == 1
 #if GL_OBJECT_COLLISION_DETECTION_TYPE == 1
     
    // Additional performance may be gained by separating isMaintained into isCenterMaintained and 
    // isOrientationMaintained, but the gain is not certain because an object that requires maintainence
    // in the first place, will likely be a mobile object that moves often.
    if ( !obbA.isMaintained )
        obbA.center = (obbA.object->getOrientation() * obbA.raw_center) + obbA.object->getPosition();
    if ( !obbB.isMaintained )
        obbB.center = (obbB.object->getOrientation() * obbB.raw_center) + obbB.object->getPosition();
 
 #endif // if GL_OBJECT_COLLISION_DETECTION_TYPE == 1   
 #endif // IMPLEMENT_BOUNDING_SPHERE == 1
    
    // Translation vector that moves the position of B relative to the position of A
    // (0, 0, 0) = PA - PA
    // T = PB – PA
    Vector3<TYPENAME> T( obbB.center - obbA.center );
    
 #if IMPLEMENT_BOUNDING_SPHERE == 1
    
    if (global::enableBoundingSphere)
    {
        if ( magnitude_squared( T ) > (obbA.radius + obbB.radius) * (obbA.radius + obbB.radius) )
            return false;
    }
    
 #if GL_OBJECT_COLLISION_DETECTION_TYPE == 1  
    if ( !obbA.isMaintained )
    {
        obbA.orientation = obbA.object->getOrientation() * obbA.raw_orientation;
        obbA.isMaintained = true;
    }
    if ( !obbB.isMaintained )
    {
        obbB.orientation = obbB.object->getOrientation() * obbB.raw_orientation;
        obbB.isMaintained = true;
    }
 #endif // GL_OBJECT_COLLISION_DETECTION_TYPE == 1       
 #endif // IMPLEMENT_BOUNDING_SPHERE == 1
 
 #if IMPLEMENT_BOUNDING_SPHERE == 0
 #if GL_OBJECT_COLLISION_DETECTION_TYPE == 1
    if ( !obbA.isMaintained )
        maintain( obbA );
    if ( !obbB.isMaintained )
        maintain( obbB );
 #endif // GL_OBJECT_COLLISION_DETECTION_TYPE == 1
 #endif // IMPLEMENT_BOUNDING_SPHERE == 0
        
 #if IMPLEMENT_AABB == 1
     if (global::enableAABB)
     {
        if ( fabs(T.x) > (obbA.half_width * fabs(obbA.orientation.Xx)) + (obbA.half_height * fabs(obbA.orientation.Yx)) 
                       + (obbA.half_depth * fabs(obbA.orientation.Zx)) 
                       + (obbB.half_width * fabs(obbB.orientation.Xx)) + (obbB.half_height * fabs(obbB.orientation.Yx)) 
                       + (obbB.half_depth * fabs(obbB.orientation.Zx)) )
                        return false;
                        
        if ( fabs(T.y) > (obbA.half_width * fabs(obbA.orientation.Xy)) + (obbA.half_height * fabs(obbA.orientation.Yy)) 
                       + (obbA.half_depth * fabs(obbA.orientation.Zy)) 
                       + (obbB.half_width * fabs(obbB.orientation.Xy)) + (obbB.half_height * fabs(obbB.orientation.Yy)) 
                       + (obbB.half_depth * fabs(obbB.orientation.Zy)) )
                        return false;
                        
        if ( fabs(T.z) > (obbA.half_width * fabs(obbA.orientation.Xz)) + (obbA.half_height * fabs(obbA.orientation.Yz)) 
                       + (obbA.half_depth * fabs(obbA.orientation.Zz))
                       + (obbB.half_width * fabs(obbB.orientation.Xz)) + (obbB.half_height * fabs(obbB.orientation.Yz)) 
                       + (obbB.half_depth * fabs(obbB.orientation.Zz)) )
                        return false;
     }
 #endif // IMPLEMENT_AABB == 1
    
    // The axes for obbA
    Vector3<TYPENAME> Ax( obbA.orientation.getXAxis() );
    Vector3<TYPENAME> Ay( obbA.orientation.getYAxis() );
    Vector3<TYPENAME> Az( obbA.orientation.getZAxis() );
    
    // The axes for obbB
    Vector3<TYPENAME> Bx( obbB.orientation.getXAxis() );
    Vector3<TYPENAME> By( obbB.orientation.getYAxis() );
    Vector3<TYPENAME> Bz( obbB.orientation.getZAxis() );
    
    // Rotation Matrix that rotates B relative to A's axes
    // I = transpose(A) * A
    // R = transpose(A) * B
    // Rij = Ai • Bj 
    OrientationMatrix3<TYPENAME> R( Ax*Bx, Ay*Bx, Az*Bx,
                                    Ax*By, Ay*By, Az*By,
                                    Ax*Bz, Ay*Bz, Az*Bz );
    
    const TYPENAME& WA( obbA.half_width );
    const TYPENAME& HA( obbA.half_height );
    const TYPENAME& DA( obbA.half_depth );
    const TYPENAME& WB( obbB.half_width );
    const TYPENAME& HB( obbB.half_height );
    const TYPENAME& DB( obbB.half_depth );
    
    // change the displacements if the displacements are equal
    if ( displacementA == displacementB )
    {
        // if the centers of both OBBs are at the same location
        if ( T.isZeroVector() )
        {
            // L = Ax
            Vector3<TYPENAME> min_axis( Ax );
            // overlapping extent in the separating axis direction
            TYPENAME min_overlap_extent( (WA + fabs( WB*R.Xx ) + fabs( HB*R.Xy ) + fabs( DB*R.Xz )) - fabs( T*Ax ) );
            
            if ( min_overlap_extent < 0.0f )
                return -1.0f;
            
            TYPENAME temp_overlap_extent( (HA + fabs( WB*R.Yx ) + fabs( HB*R.Yy ) + fabs( DB*R.Yz )) - fabs( T*Ay ) );
            
            // L = Ay
            if ( temp_overlap_extent < 0.0f )
                return -1.0f;
            else if ( temp_overlap_extent < min_overlap_extent )
            {
                min_axis = Ay;
                min_overlap_extent = temp_overlap_extent;
            }
            
            // L = Az
            temp_overlap_extent = (DA + fabs( WB*R.Zx ) + fabs( HB*R.Zy ) + fabs( DB*R.Zz )) - fabs( T*Az );
            if ( temp_overlap_extent < 0.0f )
                return -1.0f;
            else if ( temp_overlap_extent < min_overlap_extent )
            {
                min_axis = Az;
                min_overlap_extent = temp_overlap_extent;
            }
            
            // L = Bx
            temp_overlap_extent = (WB + fabs( WA*R.Xx ) + fabs( HA*R.Yx ) + fabs( DA*R.Zx )) - fabs( T*Bx );
            if ( temp_overlap_extent < 0.0f )
                return -1.0f;
            else if ( temp_overlap_extent < min_overlap_extent )
            {
                min_axis = Bx;
                min_overlap_extent = temp_overlap_extent;
            }
            
            // L = By
            temp_overlap_extent = (HB + fabs( WA*R.Xy ) + fabs( HA*R.Yy ) + fabs( DA*R.Zy )) - fabs( T*By );
            if ( temp_overlap_extent < 0.0f )
                return -1.0f;
            else if ( temp_overlap_extent < min_overlap_extent )
            {
                min_axis = By;
                min_overlap_extent = temp_overlap_extent;
            }
            
            // L = Bz
            temp_overlap_extent = (DB + fabs( WA*R.Xz ) + fabs( HA*R.Yz ) + fabs( DA*R.Zz )) - fabs( T*Bz );
            if ( temp_overlap_extent < 0.0f )
                return -1.0f;
            else if ( temp_overlap_extent < min_overlap_extent )
            {
                min_axis = Bz;
                min_overlap_extent = temp_overlap_extent;
            }
            
            // Set the displacements to be directed towards the normal of a face of an OBB that
            // has the minimum overlap between the two OBBs
            if ( T * min_axis > 0.0f )
            {
                displacementA = min_axis;
                displacementB = -min_axis;
            }
            else
            {
                displacementA = -min_axis;
                displacementB = min_axis;
            }
        }
        else
        {
            // Set the displacements of the OBBs to be directed towards one another
            displacementA = T;
            displacementB = -T;
        }
    }
    
    // netVelocity = displacementA - displacementB = netVelocityMagnitude * netUnitVelocity
    Vector3<TYPENAME> netUnitVelocity( normal( displacementA - displacementB ) );
    
    // overlapping extent in the direction of the net velocity
    // overlapping_length / | separating_axis * netUnitVelocity |
    TYPENAME min_overlapping_length( std::numeric_limits<float>::max() );
    
    // The 6 separating planes for the faces of the two boxes
    
    // The two obbs overlap past half way if the dot product between (previousPositionB - previousPositionA) 
    // and (currentPositionB - previousPositionA) is less than zero
    // obbA.previousCenter = obbA.currentCenter - displacementA
    // obbB.previousCenter = obbB.currentCenter - displacementB
    // ( (obbB.previousCenter - obbA.previousCenter) * (obbB.currentCenter - obbA.currentCenter) < 0.0f )
    //if ( (T - displacementB + displacementA) * T < 0.0f )
    //{
    /*Vector3<TYPENAME> obbA_previousProjection( (obbA.center - displacementA) * Ax );
    Vector3<TYPENAME> obbA_currentProjection( obbA.center * Ax );
    Vector3<TYPENAME> obbB_previousProjection( (obbB.center - displacementB) * Ax );
    Vector3<TYPENAME> obbB_currentProjection( obbB.center * Ax );*/
    /*TYPENAME currentCenter_ProjectionA( (obbA.center - obbB.center) * Ax );
    if ( (displacementA * Ax) * currentCenter_ProjectionA > ZERO
        || (displacementB * Ax) * currentCenter_ProjectionA < ZERO )*/
    /*if ( (displacementA * Ax) * ((obbA.center - obbB.center) * Ax) > ZERO
        || (displacementB * Ax) * ((obbB.center - obbA.center) * Ax) > ZERO )*/
    /*if ( ((T - displacementB + displacementA) * Ax) * (T * Ax) < ZERO 
        || (displacementA * Ax) * ((obbA.center - obbB.center) * Ax) > ZERO
        || (displacementB * Ax) * ((obbB.center - obbA.center) * Ax) > ZERO )*/
    /*if ( ((T - displacementB + displacementA) * Ax) * (T * Ax) < ZERO 
        || ((obbA.center - (obbA.center - displacementA)) * Ax) * ((obbA.center - obbB.center) * Ax) > ZERO
        || ((obbB.center - (obbB.center - displacementB)) * Ax) * ((obbB.center - obbA.center) * Ax) > ZERO )*/
    /*if ( (obbB_currentProjection - obbA_currentProjection) * (obbB_previousProjection - obbA_previousProjection) < ZERO 
        || (obbA_currentProjection - obbA_previousProjection) * (obbA_currentProjection - obbB_currentProjection) > ZERO
        || (obbB_currentProjection - obbB_previousProjection) * (obbB_currentProjection - obbA_currentProjection) > ZERO )*/
    // The two OBBs overlap past half way if 
    // (obbA_currentProjection - obbA_previousProjection) * (obbA_currentProjection - obbB_currentProjection) > ZERO
    // or
    // (obbB_currentProjection - obbB_previousProjection) * (obbB_currentProjection - obbA_currentProjection) > ZERO
    
    //TYPENAME half_extent_of_both_OBB( (WA + fabs( WB*R.Xx ) + fabs( HB*R.Xy ) + fabs( DB*R.Xz )) );
    
    // overlapping_length is the overlapping magnitude/length between the two OBBs
    //TYPENAME overlapping_length( half_extent_of_both_OBB - fabs( T*Ax ) );

 // #define DEBUG_COLLISION_EXTENT

    // Case 1:
    // L = Ax
    // | T*Ax | > WA + | WB * Rxx | + | HB * Rxy | + | DB * Rxz |
    Vector3<TYPENAME> L( Ax );
    TYPENAME overlapping_length( collisionExtent( obbA, obbB, displacementA, displacementB, L ) );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_length )
        {
            overlapping_length /= fabs(netUnitVelocity * L);
            if ( overlapping_length < min_overlapping_length )
                min_overlapping_length = overlapping_length;
        }
    }
    else
        return -1.0f;
    
    // Case 2:
    // L = Ay
    // | T*Ay | > HA + | WB * Ryx | + | HB * Ryy | + | DB * Ryz |
    L = Ay;
    overlapping_length = collisionExtent( obbA, obbB, displacementA, displacementB, L );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_length )
        {
            overlapping_length /= fabs(netUnitVelocity * L);
            if ( overlapping_length < min_overlapping_length )
                min_overlapping_length = overlapping_length;
        }
    }
    else
        return -1.0f;
    
    // Case 3:
    // L = Az
    // | T*Az | > DA + | WB * Rzx | + | HB * Rzy | + | DB * Rzz |
    L = Az;
    overlapping_length = collisionExtent( obbA, obbB, displacementA, displacementB, L );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_length )
        {
            overlapping_length /= fabs(netUnitVelocity * L);
            if ( overlapping_length < min_overlapping_length )
                min_overlapping_length = overlapping_length;
        }
    }
    else
        return -1.0f;
    
    // Case 4:
    // L = Bx
    // | T*Bx | > WB + | WA * Rxx | + | HA * Ryx | + | DA * Rzx |
    L = Bx;
    overlapping_length = collisionExtent( obbA, obbB, displacementA, displacementB, L );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_length )
        {
            overlapping_length /= fabs(netUnitVelocity * L);
            if ( overlapping_length < min_overlapping_length )
                min_overlapping_length = overlapping_length;
        }
    }
    else
        return -1.0f;
    
    // Case 5:
    // L = By
    // | T*By | > HB + | WA * Rxy | + | HA * Ryy | + | DA * Rzy |
    L = By;
    overlapping_length = collisionExtent( obbA, obbB, displacementA, displacementB, L );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_length )
        {
            overlapping_length /= fabs(netUnitVelocity * L);
            if ( overlapping_length < min_overlapping_length )
                min_overlapping_length = overlapping_length;
        }
    }
    else
        return -1.0f;
    
    // Case 6:
    // L = Bz
    // | T*Bz | > DB + | WA * Rxz | + | HA * Ryz | + | DA * Rzz |
    L = Bz;
    overlapping_length = collisionExtent( obbA, obbB, displacementA, displacementB, L );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_length )
        {
            overlapping_length /= fabs(netUnitVelocity * L);
            if ( overlapping_length < min_overlapping_length )
                min_overlapping_length = overlapping_length;
        }
    }
    else
        return -1.0f;
    
    // The 9 separating planes for the edges of the two boxes
    
    // Case 7:
    // L = Ax x Bx
    // |(T*Az)*Ryx - (T*Ay)*Rzx| > |HA*Rzx| + |DA*Ryx| + |HB*Rxz| + |DB*Rxy|
    L = normal( crossProduct( Ax, Bx ) );
    overlapping_length = collisionExtent( obbA, obbB, displacementA, displacementB, L );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_length )
        {
            overlapping_length /= fabs(netUnitVelocity * L);
            if ( overlapping_length < min_overlapping_length )
                min_overlapping_length = overlapping_length;
        }
    }
    else
        return -1.0f;
    
    // Case 8:
    // L = Ax x By
    // |(T*Az)*Ryy - (T*Ay)*Rzy| > |HA*Rzy| + |DA*Ryy| + |WB*Rxz| + |DB*Rxx|
    L = normal( crossProduct( Ax, By ) );
    overlapping_length = collisionExtent( obbA, obbB, displacementA, displacementB, L );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_length )
        {
            overlapping_length /= fabs(netUnitVelocity * L);
            if ( overlapping_length < min_overlapping_length )
                min_overlapping_length = overlapping_length;
        }
    }
    else
        return -1.0f;
    
    // Case 9:
    // L = Ax x Bz
    // |(T*Az)*Ryz - (T*Ay)*Rzz| > |HA*Rzz| + |DA*Ryz| + |WB*Rxy| + |HB*Rxx|
    L = normal( crossProduct( Ax, Bz ) );
    overlapping_length = collisionExtent( obbA, obbB, displacementA, displacementB, L );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_length )
        {
            overlapping_length /= fabs(netUnitVelocity * L);
            if ( overlapping_length < min_overlapping_length )
                min_overlapping_length = overlapping_length;
        }
    }
    else
        return -1.0f;
    
    // Case 10:
    // L = Ay x Bx
    // |(T*Ax)*Rzx - (T*Az)*Rxx| > |WA*Rzx| + |DA*Rxx| + |HB*Ryz| + |DB*Ryy|
    L = normal( crossProduct( Ay, Bx ) );
    overlapping_length = collisionExtent( obbA, obbB, displacementA, displacementB, L );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_length )
        {
            overlapping_length /= fabs(netUnitVelocity * L);
            if ( overlapping_length < min_overlapping_length )
                min_overlapping_length = overlapping_length;
        }
    }
    else
        return -1.0f;
    
    // Case 11:
    // L = Ay x By
    // |(T*Ax)*Rzy - (T*Az)*Rxy| > |WA*Rzy| + |DA*Rxy| + |WB*Ryz| + |DB*Ryx|
    L = normal( crossProduct( Ay, By ) );
    overlapping_length = collisionExtent( obbA, obbB, displacementA, displacementB, L );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_length )
        {
            overlapping_length /= fabs(netUnitVelocity * L);
            if ( overlapping_length < min_overlapping_length )
                min_overlapping_length = overlapping_length;
        }
    }
    else
        return -1.0f;
    
    // Case 12:
    // L = Ay x Bz
    // |(T*Ax)*Rzz - (T*Az)*Rxz| > |WA*Rzz| + |DA*Rxz| + |WB*Ryy| + |HB*Ryx|
    L = normal( crossProduct( Ay, Bz ) );
    overlapping_length = collisionExtent( obbA, obbB, displacementA, displacementB, L );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_length )
        {
            overlapping_length /= fabs(netUnitVelocity * L);
            if ( overlapping_length < min_overlapping_length )
                min_overlapping_length = overlapping_length;
        }
    }
    else
        return -1.0f;
    
    // Case 13:
    // L = Az x Bx
    // |(T*Ay)*Rxx - (T*Ax)*Ryx| > |WA*Ryx| + |HA*Rxx| + |HB*Rzz| + |DB*Rzy|
    L = normal( crossProduct( Az, Bx ) );
    overlapping_length = collisionExtent( obbA, obbB, displacementA, displacementB, L );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_length )
        {
            overlapping_length /= fabs(netUnitVelocity * L);
            if ( overlapping_length < min_overlapping_length )
                min_overlapping_length = overlapping_length;
        }
    }
    else
        return -1.0f;
    
    // Case 14:
    // L = Az x By
    // |(T*Ay)*Rxy - (T*Ax)*Ryy| > |WA*Ryy| + |HA*Rxy| + |WB*Rzz| + |DB*Rzx|
    L = normal( crossProduct( Az, By ) );
    overlapping_length = collisionExtent( obbA, obbB, displacementA, displacementB, L );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_length )
        {
            overlapping_length /= fabs(netUnitVelocity * L);
            if ( overlapping_length < min_overlapping_length )
                min_overlapping_length = overlapping_length;
        }
    }
    else
        return -1.0f;
    
    // Case 15:
    // L = Az x Bz
    // |(T*Ay)*Rxz - (T*Ax)*Ryz| > |WA*Ryz| + |HA*Rxz| + |WB*Rzy| + |HB*Rzx|
    L = normal( crossProduct( Az, Bz ) );
    overlapping_length = collisionExtent( obbA, obbB, displacementA, displacementB, L );
    if ( overlapping_length >= 0.0f )
    {
        if ( overlapping_length < min_overlapping_length )
        {
            overlapping_length /= fabs(netUnitVelocity * L);
            if ( overlapping_length < min_overlapping_length )
                min_overlapping_length = overlapping_length;
        }
    }
    else
        return -1.0f;
    
    return min_overlapping_length;
 }
 
 /**
  * collisionExtent() returns the extent the projection of the two specified OBBs onto the
  * specified axis intersect. The two specified OBBs do not intersect if the entent is less than zero. 
  * The axis does not have to be a unit vector.
  *
  * @param (const OBB<TYPENAME>&) obbA - OBB at its current position
  * @param (const OBB<TYPENAME>&) obbB - OBB at its current position
  * @param (const Vector3<TYPENAME>&) obbA_displacement - the current position of obbA minus the previous position of obbA
  * @param (const Vector3<TYPENAME>&) obbB_displacement - the current position of obbB minus the previous position of obbB
  * @param (const Vector3<TYPENAME>&) axis
  * @return bool
  */
 template <typename TYPENAME> 
 inline TYPENAME collisionExtent( const OBB<TYPENAME>& obbA, const OBB<TYPENAME>& obbB, 
             const Vector3<TYPENAME>& obbA_displacement, const Vector3<TYPENAME>& obbB_displacement, const Vector3<TYPENAME>& axis )
 {
    // for testing-only
    //if ( !collideSeparatingAxis( t, u, axis ) )
        //return -ONE;
    Vector3<TYPENAME> obbA_xAxis( obbA.orientation.getXAxis() );
    Vector3<TYPENAME> obbA_yAxis( obbA.orientation.getYAxis() );
    Vector3<TYPENAME> obbA_zAxis( obbA.orientation.getZAxis() );
    
    Vector3<TYPENAME> obbA_xLength( obbA.half_width * obbA_xAxis );
    Vector3<TYPENAME> obbA_yLength( obbA.half_height * obbA_yAxis );
    Vector3<TYPENAME> obbA_zLength( obbA.half_depth * obbA_zAxis );
    
    Vector3<TYPENAME> obbA_endpoint1( obbA.center + obbA_xLength + obbA_yLength + obbA_zLength );
    Vector3<TYPENAME> obbA_endpoint2( obbA.center + obbA_xLength + obbA_yLength - obbA_zLength );
    Vector3<TYPENAME> obbA_endpoint3( obbA.center + obbA_xLength - obbA_yLength + obbA_zLength );
    Vector3<TYPENAME> obbA_endpoint4( obbA.center - obbA_xLength + obbA_yLength + obbA_zLength );
    Vector3<TYPENAME> obbA_endpoint5( obbA.center + obbA_xLength - obbA_yLength - obbA_zLength );
    Vector3<TYPENAME> obbA_endpoint6( obbA.center - obbA_xLength - obbA_yLength + obbA_zLength );
    Vector3<TYPENAME> obbA_endpoint7( obbA.center - obbA_xLength + obbA_yLength - obbA_zLength );
    Vector3<TYPENAME> obbA_endpoint8( obbA.center - obbA_xLength - obbA_yLength - obbA_zLength );
 
    TYPENAME obbA_prev_extent1( (obbA_endpoint1 - obbA_displacement) * axis );
    TYPENAME obbA_prev_extent2( (obbA_endpoint2 - obbA_displacement) * axis );
    TYPENAME obbA_prev_extent3( (obbA_endpoint3 - obbA_displacement) * axis );
    TYPENAME obbA_prev_extent4( (obbA_endpoint4 - obbA_displacement) * axis );
    TYPENAME obbA_prev_extent5( (obbA_endpoint5 - obbA_displacement) * axis );
    TYPENAME obbA_prev_extent6( (obbA_endpoint6 - obbA_displacement) * axis );
    TYPENAME obbA_prev_extent7( (obbA_endpoint7 - obbA_displacement) * axis );
    TYPENAME obbA_prev_extent8( (obbA_endpoint8 - obbA_displacement) * axis );
    
    Vector3<TYPENAME> obbB_xAxis( obbB.orientation.getXAxis() );
    Vector3<TYPENAME> obbB_yAxis( obbB.orientation.getYAxis() );
    Vector3<TYPENAME> obbB_zAxis( obbB.orientation.getZAxis() );
    
    Vector3<TYPENAME> obbB_xLength( obbB.half_width * obbB_xAxis );
    Vector3<TYPENAME> obbB_yLength( obbB.half_height * obbB_yAxis );
    Vector3<TYPENAME> obbB_zLength( obbB.half_depth * obbB_zAxis );
    
    Vector3<TYPENAME> obbB_endpoint1( obbB.center + obbB_xLength + obbB_yLength + obbB_zLength );
    Vector3<TYPENAME> obbB_endpoint2( obbB.center + obbB_xLength + obbB_yLength - obbB_zLength );
    Vector3<TYPENAME> obbB_endpoint3( obbB.center + obbB_xLength - obbB_yLength + obbB_zLength );
    Vector3<TYPENAME> obbB_endpoint4( obbB.center - obbB_xLength + obbB_yLength + obbB_zLength );
    Vector3<TYPENAME> obbB_endpoint5( obbB.center + obbB_xLength - obbB_yLength - obbB_zLength );
    Vector3<TYPENAME> obbB_endpoint6( obbB.center - obbB_xLength - obbB_yLength + obbB_zLength );
    Vector3<TYPENAME> obbB_endpoint7( obbB.center - obbB_xLength + obbB_yLength - obbB_zLength );
    Vector3<TYPENAME> obbB_endpoint8( obbB.center - obbB_xLength - obbB_yLength - obbB_zLength );
 
    TYPENAME obbB_prev_extent1( (obbB_endpoint1 - obbB_displacement) * axis );
    TYPENAME obbB_prev_extent2( (obbB_endpoint2 - obbB_displacement) * axis );
    TYPENAME obbB_prev_extent3( (obbB_endpoint3 - obbB_displacement) * axis );
    TYPENAME obbB_prev_extent4( (obbB_endpoint4 - obbB_displacement) * axis );
    TYPENAME obbB_prev_extent5( (obbB_endpoint5 - obbB_displacement) * axis );
    TYPENAME obbB_prev_extent6( (obbB_endpoint6 - obbB_displacement) * axis );
    TYPENAME obbB_prev_extent7( (obbB_endpoint7 - obbB_displacement) * axis );
    TYPENAME obbB_prev_extent8( (obbB_endpoint8 - obbB_displacement) * axis );
    
    TYPENAME obbA_prev_min( MIN( obbA_prev_extent1, obbA_prev_extent2 ) );
    obbA_prev_min = MIN( obbA_prev_min, obbA_prev_extent3 );
    obbA_prev_min = MIN( obbA_prev_min, obbA_prev_extent4 );
    obbA_prev_min = MIN( obbA_prev_min, obbA_prev_extent5 );
    obbA_prev_min = MIN( obbA_prev_min, obbA_prev_extent6 );
    obbA_prev_min = MIN( obbA_prev_min, obbA_prev_extent7 );
    obbA_prev_min = MIN( obbA_prev_min, obbA_prev_extent8 );
    
    TYPENAME obbB_prev_min( MIN( obbB_prev_extent1, obbB_prev_extent2 ) );
    obbB_prev_min = MIN( obbB_prev_min, obbB_prev_extent3 );
    obbB_prev_min = MIN( obbB_prev_min, obbB_prev_extent4 );
    obbB_prev_min = MIN( obbB_prev_min, obbB_prev_extent5 );
    obbB_prev_min = MIN( obbB_prev_min, obbB_prev_extent6 );
    obbB_prev_min = MIN( obbB_prev_min, obbB_prev_extent7 );
    obbB_prev_min = MIN( obbB_prev_min, obbB_prev_extent8 );
    
    TYPENAME obbA_extent1( obbA_endpoint1 * axis );
    TYPENAME obbA_extent2( obbA_endpoint2 * axis );
    TYPENAME obbA_extent3( obbA_endpoint3 * axis );
    TYPENAME obbA_extent4( obbA_endpoint4 * axis );
    TYPENAME obbA_extent5( obbA_endpoint5 * axis );
    TYPENAME obbA_extent6( obbA_endpoint6 * axis );
    TYPENAME obbA_extent7( obbA_endpoint7 * axis );
    TYPENAME obbA_extent8( obbA_endpoint8 * axis );
    
    TYPENAME obbB_extent1( obbB_endpoint1 * axis );
    TYPENAME obbB_extent2( obbB_endpoint2 * axis );
    TYPENAME obbB_extent3( obbB_endpoint3 * axis );
    TYPENAME obbB_extent4( obbB_endpoint4 * axis );
    TYPENAME obbB_extent5( obbB_endpoint5 * axis );
    TYPENAME obbB_extent6( obbB_endpoint6 * axis );
    TYPENAME obbB_extent7( obbB_endpoint7 * axis );
    TYPENAME obbB_extent8( obbB_endpoint8 * axis );
    
    if ( obbB_prev_min < obbA_prev_min )
    {
        // if obbA is moving deeper into obbB in the negative direction of the axis
        if ( obbB_displacement * axis < ZERO )
        {
            TYPENAME obbB_min( MIN( obbB_extent1, obbB_extent2 ) );
            obbB_min = MIN( obbB_min, obbB_extent3 );
            obbB_min = MIN( obbB_min, obbB_extent4 );
            obbB_min = MIN( obbB_min, obbB_extent5 );
            obbB_min = MIN( obbB_min, obbB_extent6 );
            obbB_min = MIN( obbB_min, obbB_extent7 );
            obbB_min = MIN( obbB_min, obbB_extent8 );
            
            TYPENAME obbA_max( MAX( obbA_extent1, obbA_extent2 ) );
            obbA_max = MAX( obbA_max, obbA_extent3 );
            obbA_max = MAX( obbA_max, obbA_extent4 );
            obbA_max = MAX( obbA_max, obbA_extent5 );
            obbA_max = MAX( obbA_max, obbA_extent6 );
            obbA_max = MAX( obbA_max, obbA_extent7 );
            obbA_max = MAX( obbA_max, obbA_extent8 );
            
            // Notice we have to accommodate the fact that obbA may move in the negative direction of the axis
            // at a faster rate than obbB
            if ( obbA_max > obbB_min )
                // return obbA_max - obbB_min if obbA_max > obbB_min
                return obbA_max - obbB_min;
            else if ( obbA_max < obbB_min )
                // return -1 because there is no intersection
                return -ONE;
            else // obbA_max == obbB_min
                // return 0 because the intersection is at exactly 1 point
                return ZERO;
        }
        // if obbB is either moving deeper into obbA in the positive direction of the axis or not moving at all
        else // obbB_displacement * axis >= ZERO
        {
            TYPENAME obbA_min( MIN( obbA_extent1, obbA_extent2 ) );
            obbA_min = MIN( obbA_min, obbA_extent3 );
            obbA_min = MIN( obbA_min, obbA_extent4 );
            obbA_min = MIN( obbA_min, obbA_extent5 );
            obbA_min = MIN( obbA_min, obbA_extent6 );
            obbA_min = MIN( obbA_min, obbA_extent7 );
            obbA_min = MIN( obbA_min, obbA_extent8 );
            
            TYPENAME obbB_max( MAX( obbB_extent1, obbB_extent2 ) );
            obbB_max = MAX( obbB_max, obbB_extent3 );
            obbB_max = MAX( obbB_max, obbB_extent4 );
            obbB_max = MAX( obbB_max, obbB_extent5 );
            obbB_max = MAX( obbB_max, obbB_extent6 );
            obbB_max = MAX( obbB_max, obbB_extent7 );
            obbB_max = MAX( obbB_max, obbB_extent8 );
            
            if ( obbB_max > obbA_min )
                // return obbB_max - obbA_min if obbB_prev_min is before obbA_prev_min, and obbB_max > obbA_min
                return obbB_max - obbA_min;
            else if ( obbB_max < obbA_min )
                // return -1 because there is no intersection
                return -ONE;
            else // obbB_max == obbA_min
                // return 0 because the intersection is at exactly 1 point
                return ZERO;
        }
    }
    else // obbA_prev_min <= obbB_prev_min
    {
        // if triangle t is moving deeper into triangle u in the negative direction of the axis
        if ( obbA_displacement * axis < ZERO )
        {
            TYPENAME obbA_min( MIN( obbA_extent1, obbA_extent2 ) );
            obbA_min = MIN( obbA_min, obbA_extent3 );
            obbA_min = MIN( obbA_min, obbA_extent4 );
            obbA_min = MIN( obbA_min, obbA_extent5 );
            obbA_min = MIN( obbA_min, obbA_extent6 );
            obbA_min = MIN( obbA_min, obbA_extent7 );
            obbA_min = MIN( obbA_min, obbA_extent8 );
            
            TYPENAME obbB_max( MAX( obbB_extent1, obbB_extent2 ) );
            obbB_max = MAX( obbB_max, obbB_extent3 );
            obbB_max = MAX( obbB_max, obbB_extent4 );
            obbB_max = MAX( obbB_max, obbB_extent5 );
            obbB_max = MAX( obbB_max, obbB_extent6 );
            obbB_max = MAX( obbB_max, obbB_extent7 );
            obbB_max = MAX( obbB_max, obbB_extent8 );
            
            // Notice we have to accommodate the fact that obbB may move in the negative direction of the axis
            // at a faster rate than obbA
            if ( obbB_max > obbA_min )
                // return obbB_max - obbA_min if obbB_max > obbA_min
                return obbB_max - obbA_min;
            else if ( obbB_max < obbA_min )
                // return -1 because there is no intersection
                return -ONE;
            else // obbB_max == obbA_min
                // return 0 because the intersection is at exactly 1 point
                return ZERO;
        }
        // if triangle t is either moving deeper into triangle u in the positive direction of the axis or not moving at all
        else // t_displacement * axis >= ZERO
        {
            TYPENAME obbB_min( MIN( obbB_extent1, obbB_extent2 ) );
            obbB_min = MIN( obbB_min, obbB_extent3 );
            obbB_min = MIN( obbB_min, obbB_extent4 );
            obbB_min = MIN( obbB_min, obbB_extent5 );
            obbB_min = MIN( obbB_min, obbB_extent6 );
            obbB_min = MIN( obbB_min, obbB_extent7 );
            obbB_min = MIN( obbB_min, obbB_extent8 );
            
            TYPENAME obbA_max( MAX( obbA_extent1, obbA_extent2 ) );
            obbA_max = MAX( obbA_max, obbA_extent3 );
            obbA_max = MAX( obbA_max, obbA_extent4 );
            obbA_max = MAX( obbA_max, obbA_extent5 );
            obbA_max = MAX( obbA_max, obbA_extent6 );
            obbA_max = MAX( obbA_max, obbA_extent7 );
            obbA_max = MAX( obbA_max, obbA_extent8 );
            
            if ( obbA_max > obbB_min )
                // return obbA_max - obbB_min if obbA_prev_min is before obbB_prev_min, and obbA_max > obbB_min
                return obbA_max - obbB_min;
            else if ( obbA_max < obbB_min )
                // return -1 because there is no intersection
                return -ONE;
            else // obbA_max == obbB_min
                // return 0 because the intersection is at exactly 1 point
                return ZERO;
        }
    }
 }
 #endif // #if 0
 
 #if 0
 /**
  * interpolateCollision() returns the amount of time that needs to pass before the two specified 
  * OBBs collide, assuming we already know that the two OBBs will collide, and they moved back to 
  * their previous positions before the collision; the return time value is between 0 and 1 (inclusive).
  *
  * @param (OBB<TYPENAME>&) obbA
  * @param (OBB<TYPENAME>&) obbB
  * @param (const Vector3<TYPENAME>&) netVelocity
  * @return TYPENAME
  */
 template< typename TYPENAME >
 inline bool interpolateCollision( OBB<TYPENAME>& obbA, OBB<TYPENAME>& obbB, const Vector3<TYPENAME>& netVelocity )
 {
 
 #if GL_OBJECT_COLLISION_DETECTION_TYPE == 1
    if ( !obbA.isMaintained )
        maintain( obbA );
    if ( !obbB.isMaintained )
        maintain( obbB );
 #endif // GL_OBJECT_COLLISION_DETECTION_TYPE == 1
    
    // Translation vector that moves the position of B relative to the position of A
    // (0, 0, 0) = PA - PA
    // T = PB – PA
    Vector3<TYPENAME> T( obbB.center - obbA.center );
    
    // The axes for obbA
    Vector3<TYPENAME> Ax( obbA.orientation.getXAxis() );
    Vector3<TYPENAME> Ay( obbA.orientation.getYAxis() );
    Vector3<TYPENAME> Az( obbA.orientation.getZAxis() );
    
    // The axes for obbB
    Vector3<TYPENAME> Bx( obbB.orientation.getXAxis() );
    Vector3<TYPENAME> By( obbB.orientation.getYAxis() );
    Vector3<TYPENAME> Bz( obbB.orientation.getZAxis() );
    
    // Rotation Matrix that rotates B relative to A's axes
    // I = transpose(A) * A
    // R = transpose(A) * B
    // Rij = Ai • Bj 
    OrientationMatrix3<TYPENAME> R( Ax*Bx, Ay*Bx, Az*Bx,
                                    Ax*By, Ay*By, Az*By,
                                    Ax*Bz, Ay*Bz, Az*Bz );
    
    const TYPENAME& WA( obbA.half_width );
    const TYPENAME& HA( obbA.half_height );
    const TYPENAME& DA( obbA.half_depth );
    const TYPENAME& WB( obbB.half_width );
    const TYPENAME& HB( obbB.half_height );
    const TYPENAME& DB( obbB.half_depth );
    
    // The 6 separating planes for the faces of the two boxes
    
    // netVelocity = netVelocityMagnitude * netUnitVelocity
    Vector3<TYPENAME> netUnitVelocity( normal( netVelocity ) );
    TYPENAME separating_length( fabs( T*Ax ) - WA + fabs( WB*R.Xx ) + fabs( HB*R.Xy ) + fabs( DB*R.Xz ) );
    // max_separating_magnitude is the separating magnitude/length in the direction of the netUnitVelocity
    TYPENAME max_separating_magnitude( 0.0f ); // separating_length / (separating_axis * netUnitVelocity)
    TYPENAME temp;
    
    // Case 1:
    // L = Ax
    // | T*Ax | > WA + | WB * Rxx | + | HB * Rxy | + | DB * Rxz |
    if ( separating_length > 0.0f )
    {
        temp = separating_length / fabs(Ax * netUnitVelocity);
        if ( temp > max_separating_magnitude )
            max_separating_magnitude = temp;
    }
    
    // Case 2:
    // L = Ay
    // | T*Ay | > HA + | WB * Ryx | + | HB * Ryy | + | DB * Ryz |
    separating_length = fabs( T*Ay ) - HA + fabs( WB*R.Yx ) + fabs( HB*R.Yy ) + fabs( DB*R.Yz );
    if ( separating_length > 0.0f )
    {
        temp = separating_length / fabs(Ay * netUnitVelocity);
        if ( temp > max_separating_magnitude )
            max_separating_magnitude = temp;
    }
    
    // Case 3:
    // L = Az
    // | T*Az | > DA + | WB * Rzx | + | HB * Rzy | + | DB * Rzz |
    separating_length = fabs( T*Az ) - DA + fabs( WB*R.Zx ) + fabs( HB*R.Zy ) + fabs( DB*R.Zz );
    if ( separating_length > 0.0f )
    {
        temp = separating_length / fabs(Az * netUnitVelocity);
        if ( temp > max_separating_magnitude )
            max_separating_magnitude = temp;
    }
    
    // Case 4:
    // L = Bx
    // | T*Bx | > WB + | WA * Rxx | + | HA * Ryx | + | DA * Rzx |
    separating_length = fabs( T*Bx ) - WB + fabs( WA*R.Xx ) + fabs( HA*R.Yx ) + fabs( DA*R.Zx );
    if ( separating_length > 0.0f )
    {
        temp = separating_length / fabs(Bx * netUnitVelocity);
        if ( temp > max_separating_magnitude )
            max_separating_magnitude = temp;
    }
    
    // Case 5:
    // L = By
    // | T*By | > HB + | WA * Rxy | + | HA * Ryy | + | DA * Rzy |
    separating_length = fabs( T*By ) - HB + fabs( WA*R.Xy ) + fabs( HA*R.Yy ) + fabs( DA*R.Zy );
    if ( separating_length > 0.0f )
    {
        temp = separating_length / fabs(By * netUnitVelocity);
        if ( temp > max_separating_magnitude )
            max_separating_magnitude = temp;
    }
    
    // Case 6:
    // L = Bz
    // | T*Bz | > DB + | WA * Rxz | + | HA * Ryz | + | DA * Rzz |
    separating_length = fabs( T*Bz ) - DB + fabs( WA*R.Xz ) + fabs( HA*R.Yz ) + fabs( DA*R.Zz );
    if ( separating_length > 0.0f )
    {
        temp = separating_length / fabs(Bz * netUnitVelocity);
        if ( temp > max_separating_magnitude )
            max_separating_magnitude = temp;
    }
    
    // The 9 separating planes for the edges of the two boxes
    
    // Case 7:
    // L = Ax x Bx
    // |(T*Az)*Ryx - (T*Ay)*Rzx| > |HA*Rzx| + |DA*Ryx| + |HB*Rxz| + |DB*Rxy|
    separating_length = fabs( (T*Az*R.Yx) - (T*Ay*R.Zx) )
                            - fabs( HA*R.Zx ) + fabs( DA*R.Yx ) + fabs( HB*R.Xz ) + fabs( DB*R.Xy );
    if ( separating_length > 0.0f )
    {
        temp = separating_length / fabs(normal( crossProduct( Ax, Bx ) ) * netUnitVelocity);
        if ( temp > max_separating_magnitude )
            max_separating_magnitude = temp;
    }
    
    // Case 8:
    // L = Ax x By
    // |(T*Az)*Ryy - (T*Ay)*Rzy| > |HA*Rzy| + |DA*Ryy| + |WB*Rxz| + |DB*Rxx|
    separating_length = fabs( (T*Az*R.Yy) - (T*Ay*R.Zy) ) 
                            - fabs( HA*R.Zy ) + fabs( DA*R.Yy ) + fabs( WB*R.Xz ) + fabs( DB*R.Xx );
    if ( separating_length > 0.0f )
    {
        temp = separating_length / fabs(normal( crossProduct( Ax, By ) ) * netUnitVelocity);
        if ( temp > max_separating_magnitude )
            max_separating_magnitude = temp;
    }
    
    // Case 9:
    // L = Ax x Bz
    // |(T*Az)*Ryz - (T*Ay)*Rzz| > |HA*Rzz| + |DA*Ryz| + |WB*Rxy| + |HB*Rxx|
    separating_length = fabs( (T*Az*R.Yz) - (T*Ay*R.Zz) ) 
                            - fabs( HA*R.Zz ) + fabs( DA*R.Yz ) + fabs( WB*R.Xy ) + fabs( HB*R.Xx );
    if ( separating_length > 0.0f )
    {
        temp = separating_length / fabs(normal( crossProduct( Ax, Bz ) ) * netUnitVelocity);
        if ( temp > max_separating_magnitude )
            max_separating_magnitude = temp;
    }
    
    // Case 10:
    // L = Ay x Bx
    // |(T*Ax)*Rzx - (T*Az)*Rxx| > |WA*Rzx| + |DA*Rxx| + |HB*Ryz| + |DB*Ryy|
    separating_length = fabs( (T*Ax*R.Zx) - (T*Az*R.Xx) )
                            - fabs( WA*R.Zx ) + fabs( DA*R.Xx ) + fabs( HB*R.Yz ) + fabs( DB*R.Yy );
    if ( separating_length > 0.0f )
    {
        temp = separating_length / fabs(normal( crossProduct( Ay, Bx ) ) * netUnitVelocity);
        if ( temp > max_separating_magnitude )
            max_separating_magnitude = temp;
    }
    
    // Case 11:
    // L = Ay x By
    // |(T*Ax)*Rzy - (T*Az)*Rxy| > |WA*Rzy| + |DA*Rxy| + |WB*Ryz| + |DB*Ryx|
    separating_length = fabs( (T*Ax*R.Zy) - (T*Az*R.Xy) ) 
                            - fabs( WA*R.Zy ) + fabs( DA*R.Xy ) + fabs( WB*R.Yz ) + fabs( DB*R.Yx );
    if ( separating_length > 0.0f )
    {
        temp = separating_length / fabs(normal( crossProduct( Ay, By ) ) * netUnitVelocity);
        if ( temp > max_separating_magnitude )
            max_separating_magnitude = temp;
    }
    
    // Case 12:
    // L = Ay x Bz
    // |(T*Ax)*Rzz - (T*Az)*Rxz| > |WA*Rzz| + |DA*Rxz| + |WB*Ryy| + |HB*Ryx|
    separating_length = fabs( (T*Ax*R.Zz) - (T*Az*R.Xz) ) 
                            - fabs( WA*R.Zz ) + fabs( DA*R.Xz ) + fabs( WB*R.Yy ) + fabs( HB*R.Yx );
    if ( separating_length > 0.0f )
    {
        temp = separating_length / fabs(normal( crossProduct( Ay, Bz ) ) * netUnitVelocity);
        if ( temp > max_separating_magnitude )
            max_separating_magnitude = temp;
    }
    
    // Case 13:
    // L = Az x Bx
    // |(T*Ay)*Rxx - (T*Ax)*Ryx| > |WA*Ryx| + |HA*Rxx| + |HB*Rzz| + |DB*Rzy|
    separating_length = fabs( (T*Ay*R.Xx) - (T*Ax*R.Yx) ) 
                            - fabs( WA*R.Yx ) + fabs( HA*R.Xx ) + fabs( HB*R.Zz ) + fabs( DB*R.Zy );
    if ( separating_length > 0.0f )
    {
        temp = separating_length / fabs(normal( crossProduct( Az, Bx ) ) * netUnitVelocity);
        if ( temp > max_separating_magnitude )
            max_separating_magnitude = temp;
    }
    
    // Case 14:
    // L = Az x By
    // |(T*Ay)*Rxy - (T*Ax)*Ryy| > |WA*Ryy| + |HA*Rxy| + |WB*Rzz| + |DB*Rzx|
    separating_length = fabs( (T*Ay*R.Xy) - (T*Ax*R.Yy) ) 
                            - fabs( WA*R.Yy ) + fabs( HA*R.Xy ) + fabs( WB*R.Zz ) + fabs( DB*R.Zx );
    if ( separating_length > 0.0f )
    {
        temp = separating_length / fabs(normal( crossProduct( Az, By ) ) * netUnitVelocity);
        if ( temp > max_separating_magnitude )
            max_separating_magnitude = temp;
    }
    
    // Case 15:
    // L = Az x Bz
    // |(T*Ay)*Rxz - (T*Ax)*Ryz| > |WA*Ryz| + |HA*Rxz| + |WB*Rzy| + |HB*Rzx|
    separating_length = fabs( (T*Ay*R.Xz) - (T*Ax*R.Yz) )
                            - fabs( WA*R.Yz ) + fabs( HA*R.Xz ) + fabs( WB*R.Zy ) + fabs( HB*R.Zx );
    if ( separating_length > 0.0f )
    {
        temp = separating_length / fabs(normal( crossProduct( Az, Bz ) ) * netUnitVelocity);
        if ( temp > max_separating_magnitude )
            max_separating_magnitude = temp;
    }
    
    // separating_time = max_separating_magnitude / netVelocityMagnitude 
    return max_separating_magnitude / magnitude( netVelocity );
 }
 #endif // #if 0
 #endif // IMPLEMENT_INTERPOLATION == 1
 
 /**
  * maintain() recomputes the center and orientation using the raw_center, raw_orientation, and
  * the position and orientation of the actual object.
  *
  * @param (OBB<TYPENAME>&) obb
  * @return OBB<TYPENAME>&
  */
 template <typename TYPENAME>
 inline OBB<TYPENAME>& maintain( OBB<TYPENAME>& obb )
 {
    const OrientationMatrix3<TYPENAME>& obj_orientation( obb.object->getOrientation() );
    obb.center = (obj_orientation * obb.raw_center) + obb.object->getPosition();
    obb.orientation = obj_orientation * obb.raw_orientation;
    
 #if GL_OBJECT_COLLISION_DETECTION_TYPE == 1
    obb.isMaintained = true;
 #endif // GL_OBJECT_COLLISION_DETECTION_TYPE == 1
 
    return obb;
 }
 
 /**
  * print() prints out information about this OBB.
  *
  * @param (const OBB<TYPENAME>&) obb
  */
 template <typename TYPENAME>
 inline void print( const OBB<TYPENAME>& obb )
 {
    printf("OBB\n");
    printf("    half_width: %1f\n", obb.half_width);
    printf("    half_height: %1f\n", obb.half_height);
    printf("    half_depth: %1f\n", obb.half_depth);
    printf("    center: ");
    print( obb.center );
    printf("    orientation: [%1f %2f %3f\n                  %4f %5f %6f\n                  %7f %8f %9f]\n", 
                             obb.orientation.Xx, obb.orientation.Yx, obb.orientation.Zx, 
                             obb.orientation.Xy, obb.orientation.Yy, obb.orientation.Zy, 
                             obb.orientation.Xz, obb.orientation.Yz, obb.orientation.Zz );
 }
 
 /**
  * render() contains the code necessary to render an OBB in OpenGL.
  *
  * @param (const OBB<TYPENAME>&) obb
  */
 template <typename TYPENAME>
 inline void render( const OBB<TYPENAME>& obb )
 {
    const OrientationMatrix3<TYPENAME>& om( obb.orientation ); //obb.getOrientation() ); //obb.orientation ); 
    //const OrientationMatrix3<TYPENAME>& om( obb.object->getOrientation() * obb.raw_orientation );
    
    glColor3f( 1.0f, 1.0f, 1.0f );
    Render_Shape::Box_WireFrame( obb.center, 
                                 obb.half_width, obb.half_height, obb.half_depth,
                                 om.Xx, om.Yx, om.Zx,
                                 om.Xy, om.Yy, om.Zy,
                                 om.Xz, om.Yz, om.Zz );
    /*Render_Shape::Box_Solid( obb.center, 
                                 Vector3<TYPENAME>(obb.half_width, obb.half_height, obb.half_depth),
                                 om );*/
    //printf("OBB - object position");
    //print( obb.object->getPosition() );                          
    /*Render_Shape::Box_WireFrame( (obb.object->getOrientation()*obb.raw_center) + obb.object->getPosition(), 
                                 obb.half_width, obb.half_height, obb.half_depth,
                                 om.Xx, om.Yx, om.Zx,
                                 om.Xy, om.Yy, om.Zy,
                                 om.Xz, om.Yz, om.Zz );*/
 #if IMPLEMENT_BOUNDING_SPHERE == 1
 if (global::enableBoundingSphere)
    renderBoundingSphere( obb );
 #endif // IMPLEMENT_BOUNDING_SPHERE == 1
 #if IMPLEMENT_AABB == 1
 if (global::enableAABB)
    renderAABB( obb );
 #endif // IMPLEMENT_AABB == 1
 }
 
 #if IMPLEMENT_BOUNDING_SPHERE == 1
 /**
  * renderBoundingSphere() contains the code necessary to render the bounding sphere for the specified obb in OpenGL.
  *
  * @param (const OBB<TYPENAME>&) obb
  */
 template <typename TYPENAME> 
 inline void renderBoundingSphere( const OBB<TYPENAME>& obb )
 {
    glPushMatrix();
        glTranslatef( obb.center.x, obb.center.y, obb.center.z );
        glColor3f( 0.0f, 0.0f, 1.0f );
        glutWireSphere( obb.radius, 50, 15 );
    glPopMatrix();
 }
 #endif // IMPLEMENT_BOUNDING_SPHERE == 1
 
 #if IMPLEMENT_AABB == 1
 /**
  * renderAABB() contains the code necessary to render the AABB for the specified obb in OpenGL.
  *
  * @param (const OBB<TYPENAME>&) obb
  */
 template <typename TYPENAME>
 inline void renderAABB( const OBB<TYPENAME>& obb )
 {
    const OrientationMatrix3<TYPENAME>& om( obb.orientation );
    
    glColor3f( 0.0f, 1.0f, 0.0f );
    Render_Shape::Box_WireFrame( obb.center, 
                                 (obb.half_width*fabs(om.Xx)) + (obb.half_height*fabs(om.Yx)) + (obb.half_depth*fabs(om.Zx)), 
                                 (obb.half_width*fabs(om.Xy)) + (obb.half_height*fabs(om.Yy)) + (obb.half_depth*fabs(om.Zy)),
                                 (obb.half_width*fabs(om.Xz)) + (obb.half_height*fabs(om.Yz)) + (obb.half_depth*fabs(om.Zz)),
                                 1.0f, 0.0f, 0.0f,
                                 0.0f, 1.0f, 0.0f,
                                 0.0f, 0.0f, 1.0f );
 }
 #endif // IMPLEMENT_AABB == 1
 
 #endif // OBB_H